検索
ホームページJava&#&チュートリアルJVM の 4 つの参照状態の紹介

JVM の 4 つの参照状態の紹介

Jun 28, 2017 am 10:00 AM
java引用

JVM の 4 つの参照状態

「Java 仮想マシン 5: Java ガベージ コレクション (GC) メカニズムの詳細な説明」の記事で、JVM の 4 つの参照状態について簡単に説明しました。は簡単に学んだばかりで、4 つの基準状態についてほとんど理解していない状態で、そのような概念があることを知りました。ここ 2 日間で仮想マシンの部分を読み直して、いくつかの JVM リファレンスを詳しく調べるためにサンプルをたくさん書きました。JVM リファレンスについての理解がかなり深まったので、それを要約して共有するために記事を書きました。

まず、JVM の 4 つの参照状態から始めましょう。この部分は、Zhou Zhiming 氏の書籍『Java 仮想マシンの詳細理解: JVM の高度な機能とベスト プラクティス』からの抜粋です。

JDK1.2 より前の Java における参照の定義は非常に伝統的でした: 参照型データに格納された値が別のメモリの開始アドレスを表す場合、このメモリは参照を表すと言われます 。この定義は非常に純粋ですが、オブジェクトはこの方法で参照できるか参照できないことしかできず、「食べるには味がなく、捨てるには惜しい」オブジェクトを説明するのは無力です。私たちはそのようなタイプのオブジェクトを記述したいと考えています: メモリ空間がまだ十分にある場合は、メモリ空間に保持できますが、ガベージ コレクション後にメモリ空間がまだ非常に不足している場合は、これらのオブジェクトを破棄できます (前段落と同じ青文字の対照学習)。多くのシステムのキャッシュ機能は、この参照シナリオに準拠しています。

JDK1.2以降、Javaでは参照の概念が拡張され、参照が強参照、ソフト参照、弱参照、ファントム参照の4種類に分けられ、この4つの引用強度が一気に弱まりました。

  • 強い参照とは、「Object obj = new Object()」と同様に、プログラム コード内に遍在する参照を指します。 強い参照が存在する限り、ガベージ コレクターはそれらを再利用することはありません。オブジェクト

  • ソフト参照は、便利ではあるが必須ではないいくつかのオブジェクトを記述するために使用されます ソフト参照に関連付けられたオブジェクトの場合、これらはシステムがメモリ オーバーフロー例外を経験する前に保存されます。オブジェクトは2回目のリサイクルのリサイクル範囲に含まれます。このリサイクルに十分なメモリがない場合、メモリ オーバーフロー例外がスローされます。 JDK1.2以降、ソフト参照を実装するためにSoftReferenceクラスが提供されています

  • 弱参照は非必須オブジェクトの記述にも使用されますが、その強度はソフト参照より弱いです

    、弱参照に関連付けられたオブジェクト 次のガベージ コレクションが発生するまでのみ存続できます。ガベージ コレクターが動作すると、現在のメモリが十分であるかどうかに関係なく、弱い参照のみに関連付けられたオブジェクトがリサイクルされます。 JDK1.2 以降、弱参照を実装するために WeakReference クラスが提供されます。仮想参照も、最も弱い参照関係であるゴースト参照またはファントム参照になります。オブジェクトに仮想参照があるかどうかはその存続期間には影響せず、仮想参照を通じてオブジェクト インスタンスを取得することは不可能です。 オブジェクトの仮想参照関連付けを設定する唯一の目的は、オブジェクトがコレクターによってリサイクルされたときにシステム通知を受け取ることです

    。 JDK1.2 以降、仮想参照を実装するために PhantomReference クラスが提供されています
  • コードを開始する前に書かれています

コードを通していくつかの参照状態を調べる前に、最初にいくつかのパラメータと以下のすべてを定義します一部のコード例では、これらのパラメーターを使用します。 最初は JVM パラメーターで、ここでは次を使用します:

-Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+UseParNewGC -verbose:gc -XX:+PrintGCDetails

这意味着:

  • 堆大小固定为20M

  • 新生代大小为10M,SurvivorRatio设置为8,则Eden区大小=8M,每个Survivor区大小=1M,每次有9M的新生代内存空间可用来new对象

  • 新生代使用使用ParNew收集器,Server模式下默认是Parallel收集器,不过这个收集器的GC日志我看着没有ParNew收集器的GC日志舒服,因此就改成ParNew收集器了

  • 当发生GC的时候打印GC的简单信息,当程序运行结束打印GC详情

其次,再定义一个常量类"_1MB":

    _1MB = 1024 * 1024

代码示例使用byte数组,每个byte为1个字节,因此定义一个"_1MB"的常量就可以方便得到1M、2M、3M...的内存空间了。

 

强引用的研究

关于强引用的研究,研究的重点在于验证"当一个对象到GC Roots没有任何引用链相连,则证明此对象是不可用的(要被回收)"这句话的正确性。虚拟机参数上面列了,首先写一个空方法:

 1 /** 2  * @author 五月的仓颉 3  */ 4 @Test 5 public void testStrongReference0() { 6  7 }

程序运行结果为:

 1 Heap 2  par new generation   total 9216K, used 3699K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000) 3   eden space 8192K,  45% used [0x00000000f9a00000, 0x00000000f9d9cdc0, 0x00000000fa200000) 4   from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000) 5   to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000) 6  tenured generation   total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000) 7    the space 10240K,   0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000) 8  compacting perm gen  total 21248K, used 4367K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000) 9    the space 21248K,  20% used [0x00000000fae00000, 0x00000000fb243d88, 0x00000000fb243e00, 0x00000000fc2c0000)10 No shared spaces configured.

这意味着新生代中本身就有3699K的内存空间。很好理解,因为虚拟机启动的时候就会加载一部分数据到内存中,这部分数据的大小为3699K。

下一步我们放一个4M的byte数组进去(4M是因为找一个相对大点的数字,结果会比较明显),4M=4096K,加上原来的3966K等于8062K。对这8062K内存空间触发一次GC:

 1 /** 2  * @author 五月的仓颉 3  */ 4 @Test 5 public void testStrongReference0() { 6     System.out.println("**********强引用测试(放一个4M的数组,触发GC)**********"); 7          8     byte[] bytes = new byte[4 * _1MB]; 9         10     // 手动触发GC11     System.gc();12 }

运行结果为:

 1 **********强引用测试(放一个4M的数组,触发GC)********** 2 [Full GC[Tenured: 0K->5161K(10240K), 0.0085630 secs] 7958K->5161K(19456K), [Perm : 4354K->4354K(21248K)], 0.0086002 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
 3 Heap 4  par new generation   total 9216K, used 284K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000) 5   eden space 8192K,   3% used [0x00000000f9a00000, 0x00000000f9a47300, 0x00000000fa200000) 6   from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000) 7   to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000) 8  tenured generation   total 10240K, used 5161K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000) 9    the space 10240K,  50% used [0x00000000fa400000, 0x00000000fa90a548, 0x00000000fa90a600, 0x00000000fae00000)10  compacting perm gen  total 21248K, used 4367K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)11    the space 21248K,  20% used [0x00000000fae00000, 0x00000000fb243dc0, 0x00000000fb243e00, 0x00000000fc2c0000)12 No shared spaces configured.

总结一下这次GC的结果(由于这篇不是研究内存分配的文章,因此我们只关注结果,至于过程到底为什么就不细究了):

  • 新生代中只留下了284K大小的对象

  • 7958K大小的对象被移到了老年代中

  • 7958K大小的对象被进行了一次回收,剩余5161K大小的对象

总结起来就是4M的byte数组并没有被回收(因为总共有5161K的对象,虚拟机启动的时候也才加载了3699K不到5161K,那4M的byte数组肯定是在的),原因是有bytes引用指向4M的byte数组。既然如此,我们把bytes置空看看结果如何:

 1 /** 2  * @author 五月的仓颉 3  */ 4 @Test 5 public void testStrongReference0() { 6     System.out.println("**********强引用测试(放一个4M的数组,bytes置空,触发GC)**********"); 7          8     byte[] bytes = new byte[4 * _1MB]; 9         10     bytes = null;11         12     // 手动触发GC13     System.gc();14 }

运行结果为:

 1 **********强引用测试(放一个4M的数组,bytes置空,触发GC)********** 2 [Full GC[Tenured: 0K->1064K(10240K), 0.0096213 secs] 7958K->1064K(19456K), [Perm : 4354K->4354K(21248K)], 0.0096644 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
 3 Heap 4  par new generation   total 9216K, used 284K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000) 5   eden space 8192K,   3% used [0x00000000f9a00000, 0x00000000f9a47300, 0x00000000fa200000) 6   from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000) 7   to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000) 8  tenured generation   total 10240K, used 1064K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000) 9    the space 10240K,  10% used [0x00000000fa400000, 0x00000000fa50a368, 0x00000000fa50a400, 0x00000000fae00000)10  compacting perm gen  total 21248K, used 4367K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)11    the space 21248K,  20% used [0x00000000fae00000, 0x00000000fb243dc0, 0x00000000fb243e00, 0x00000000fc2c0000)12 No shared spaces configured.

从GC详情我们可以看到:

  • 老年代只使用了1064K大小的内存

  • 新生代只使用了284K大小的内存

很显然4M的byte数组被回收

由这个例子我们回顾可以作为GC Roots的对象:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象,比如在方法中定义"Object obj = new Object();"

  • 方法区中类静态属性引用的对象,比如在类中定义"private static Object lock = new Object();",将Object对象作为一个锁,所有类共享

  • 方法区中常量引用的对象,比如在接口中定义"public static final char c = 'a';",字符'a'是一个常量

  • 本地方法栈中JNI(即一般说的Native方法)引用的对象,这个不好找例子

这次的回收正是因为第一条。本身有bytes(在虚拟机栈中)指向4M的byte数组,由于将bytes置空。因此4M的byte数组此时没有任何一个可以作为GC Roots对象的引用指向它,即4M的byte数组被虚拟机标记为可回收的垃圾,在GC时被回收。

稍微扩展一下,这里上面代码的做法是手动将bytes置空,其实方法调用结束也是一样的,栈帧消失,栈帧消失意味着bytes消失,那么4M的byte数组同样没有任何一个可以作为GC Roots对象的引用指向它,因此方法调用结束之后,4M的byte数组同样会被虚拟机标记为可回收的垃圾,在GC时被回收。

 

软引用的研究

软引用之前说过了,JDK提供了SoftReference类共开发者使用,那我们就利用SoftReference研究一下软引用,测试代码为:

 1 /** 2  * @author 五月的仓颉 3  */ 4 @Test 5 public void testSoftReference0() { 6     System.out.println("**********软引用测试**********"); 7          8     byte[] bytes = new byte[4 * _1MB]; 9     SoftReference<byte> sr = new SoftReference<byte>(bytes);10     System.out.println("GC前:" + sr.get());11         12     bytes = null;13         14     System.gc();15     System.out.println("GC后:" + sr.get());16 }</byte></byte>

同样的new一个4M的byte数组,通过SoftReference构造方法放到SoftReference中。

这段代码最值得注意的是第9行"bytes=null"这一句,如果不将bytes置空,那么4M的byte数组还与强引用关联着,内存不够虚拟机将抛出异常而不会尝试回收它;将bytes置空则不一样,4M的byte数组失去了强引用,但是它又在SoftReference中,这意味着这个4M的byte数组目前仅仅与软引用关联

运行一下程序,结果为:

 1 **********软引用测试********** 2 GC前:[B@76404629 3 [Full GC[Tenured: 0K->5161K(10240K), 0.0094088 secs] 7953K->5161K(19456K), [Perm : 4354K->4354K(21248K)], 0.0094428 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
 4 GC后:[B@76404629 5 Heap 6  par new generation   total 9216K, used 284K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000) 7   eden space 8192K,   3% used [0x00000000f9a00000, 0x00000000f9a47330, 0x00000000fa200000) 8   from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000) 9   to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)10  tenured generation   total 10240K, used 5161K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)11    the space 10240K,  50% used [0x00000000fa400000, 0x00000000fa90a778, 0x00000000fa90a800, 0x00000000fae00000)12  compacting perm gen  total 21248K, used 4367K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)13    the space 21248K,  20% used [0x00000000fae00000, 0x00000000fb243f10, 0x00000000fb244000, 0x00000000fc2c0000)14 No shared spaces configured.

看到GC前后,bytes都是"[B@76404629",很显然4M的byte数组并没有被回收。从内存空间来看,老年代中使用了5161K,和之前强引用测试是一样的,证明了这一点。

那我们怎么能看到弱引用的回收呢?既然弱引用是发生在内存不够之前,那只需要不断实例化byte数组,然后将之与软引用关联即可,代码为:

 1 /** 2  * @author 五月的仓颉 3  */ 4 @Test 5 public void testSoftReference1() { 6     System.out.println("**********软引用测试**********"); 7              8     SoftReference<byte> sr0 = new SoftReference<byte>(new byte[4 * _1MB]); 9     SoftReference<byte> sr1 = new SoftReference<byte>(new byte[4 * _1MB]);10     SoftReference<byte> sr2 = new SoftReference<byte>(new byte[4 * _1MB]);11     SoftReference<byte> sr3 = new SoftReference<byte>(new byte[4 * _1MB]);12     SoftReference<byte> sr4 = new SoftReference<byte>(new byte[4 * _1MB]);13     SoftReference<byte> sr5 = new SoftReference<byte>(new byte[4 * _1MB]);14             15     System.out.println(sr0.get());16     System.out.println(sr1.get());17     System.out.println(sr2.get());18     System.out.println(sr3.get());19     System.out.println(sr4.get());20     System.out.println(sr5.get());21 }</byte></byte></byte></byte></byte></byte></byte></byte></byte></byte></byte></byte>

运行结果为:

 1 **********软引用测试********** 2 [GC[ParNew: 7958K->1024K(9216K), 0.0041103 secs] 7958K->5187K(19456K), 0.0041577 secs] [Times: user=0.05 sys=0.00, real=0.00 secs] 
 3 [GC[ParNew: 5203K->331K(9216K), 0.0036532 secs] 9366K->9481K(19456K), 0.0036694 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
 4 [GC[ParNew: 4427K->4427K(9216K), 0.0000249 secs][Tenured: 9149K->9149K(10240K), 0.0054937 secs] 13577K->13246K(19456K), [Perm : 4353K->4353K(21248K)], 0.0055600 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
 5 [Full GC[Tenured: 9149K->783K(10240K), 0.0071252 secs] 13246K->783K(19456K), [Perm : 4353K->4352K(21248K)], 0.0071560 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
 6 [GC[ParNew: 4096K->41K(9216K), 0.0010362 secs] 4879K->4921K(19456K), 0.0010745 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
 7 [GC[ParNew: 4137K->10K(9216K), 0.0009216 secs] 9017K->8986K(19456K), 0.0009366 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
 8 null 9 null10 null11 [B@4783165b12 [B@6f30d50a13 [B@6ef2bc8d14 Heap15  par new generation   total 9216K, used 4307K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)16   eden space 8192K,  52% used [0x00000000f9a00000, 0x00000000f9e32560, 0x00000000fa200000)17   from space 1024K,   1% used [0x00000000fa200000, 0x00000000fa202978, 0x00000000fa300000)18   to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)19  tenured generation   total 10240K, used 8975K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)20    the space 10240K,  87% used [0x00000000fa400000, 0x00000000facc3f40, 0x00000000facc4000, 0x00000000fae00000)21  compacting perm gen  total 21248K, used 4366K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)22    the space 21248K,  20% used [0x00000000fae00000, 0x00000000fb2439e0, 0x00000000fb243a00, 0x00000000fc2c0000)23 No shared spaces configured.

从第8行~第13行的结果来看,前三个4M的byte数组被回收了,后三个4M的byte数组还在,这就证明了"被软引用关联的对象会在内存不够时被回收"。

这段代码我们可以做一个对比思考:

  • 如果4M的byte数组没有被软引用关联而是被强引用关联,且不释放强引用,那么new到第4个4M的byte数组时就会报错,因为老年代总共只有10M,前两个4M的byte数组可以进入老年代,第3个4M的byte数组new出来的时候放入新生代,但是当第四个4M的byte数组new出来的时候,第3个4M的byte数组却没法进入老年代(因为3个4M=12M,大于老年代的10M),虚拟机抛出OutOfMemoryError

  • 如果4M的byte数组被软引用关联且强引用已经释放,那么可以无限写"SoftReference sr = new SoftReference(new byte[4 * _1MB]);"这句代码,因为内存不够了就回收4M的byte数组,永远没有内存溢出的可能

所以,很多时候对一些非必需的对象,我们可以将直接将其与软引用关联,这样内存不够时会先回收软引用关联的对象而不会抛出OutOfMemoryError,毕竟抛出OutOfMemoryError意味着整个应用将停止运行。

 

弱引用的研究

JDK给我们提供的了WeakReference用以将一个对象关联到弱引用,弱引用的测试比较简单,代码为:

 1 /** 2  * @author 五月的仓颉 3  */ 4 @Test 5 public void testWeakReference() { 6     System.out.println("**********弱引用测试**********"); 7          8     WeakReference<byte> wr = new WeakReference<byte>(new byte[4 * _1MB]); 9     System.out.println("GC前:" + wr.get());10         11     System.gc();12     System.out.println("GC后:" + wr.get());13 }</byte></byte>

我这里不定义一个强引用直接关联4M的byte数组(避免忘了将对象与强引用的关联取消),这也是使用SoftReference、WeakReference时我个人比较推荐的做法。程序运行的结果为:

 1 **********弱引用测试********** 2 GC前:[B@21dd63a8 3 [Full GC[Tenured: 0K->1065K(10240K), 0.0080353 secs] 7958K->1065K(19456K), [Perm : 4353K->4353K(21248K)], 0.0080894 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
 4 GC后:null 5 Heap 6  par new generation   total 9216K, used 284K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000) 7   eden space 8192K,   3% used [0x00000000f9a00000, 0x00000000f9a47318, 0x00000000fa200000) 8   from space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000) 9   to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)10  tenured generation   total 10240K, used 1065K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)11    the space 10240K,  10% used [0x00000000fa400000, 0x00000000fa50a6e8, 0x00000000fa50a800, 0x00000000fae00000)12  compacting perm gen  total 21248K, used 4367K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)13    the space 21248K,  20% used [0x00000000fae00000, 0x00000000fb243dc8, 0x00000000fb243e00, 0x00000000fc2c0000)14 No shared spaces configured.

看到GC后bytes为null了,且新生代、老年代中也没见到有4M以上的大对象,从两个角度都证明了,GC之后4M的byte数组被回收了。

 

Reference与ReferenceQueue

前面用代码验证了强引用、软应用、弱引用三种引用状态,虚引用就不演示了,记住虚引用是用于跟踪对象的回收状态就够了。

下面再讲一个知识点ReferenceQueue,ReferenceQueue的作用分点讲解下:

  1. SoftReference、WeakReference、PhantomReference,在构造的时候可以通过构造函数传入一个ReferenceQueue,但是只有PhantomReference,ReferenceQueue是必须的

  2. 以SoftReference为例,一个类型为SoftReference的sr关联了一个4M的byte数组,那么当内存不够的时候,回收此4M的byte数组,sr.get()为null,表示sr不再关联此4M的byte数组

  3. 当sr对应的4M的byte数组被回收之后,sr本身被加入ReferenceQueue中,表示此软引用关联的对象被回收

  4. ReferenceQueue本身是一个Queue,可通过poll()方法不断拿到队列的头元素,如果是null表示没有被回收的软引用关联的对象,如果不是null表示有软引用关联的对象被回收

  5. SoftReference是这样的,WeakReference与PhantomReference同理

讲完理论,用代码验证一下,还是使用软引用,不过为了显示更清楚把GC显示相关参数(-verbose:gc  -XX:+PrintGCDetails)去掉,代码为:

 1 /** 2  * @author 五月的仓颉 3  */ 4 @Test 5 public void testReferenceQueue() { 6     System.out.println("**********引用队列测试**********\n"); 7          8     ReferenceQueue<byte> referenceQueue = new ReferenceQueue<byte>(); 9         10     SoftReference<byte> sr0 = new SoftReference<byte>(new byte[4 * _1MB], referenceQueue);11     SoftReference<byte> sr1 = new SoftReference<byte>(new byte[4 * _1MB], referenceQueue);12     SoftReference<byte> sr2 = new SoftReference<byte>(new byte[4 * _1MB], referenceQueue);13     SoftReference<byte> sr3 = new SoftReference<byte>(new byte[4 * _1MB], referenceQueue);14     SoftReference<byte> sr4 = new SoftReference<byte>(new byte[4 * _1MB], referenceQueue);15     SoftReference<byte> sr5 = new SoftReference<byte>(new byte[4 * _1MB], referenceQueue);16         17     System.out.println("**********软引用关联的对象展示**********");18     System.out.println(sr0 + "---" + sr0.get());19     System.out.println(sr1 + "---" + sr1.get());20     System.out.println(sr2 + "---" + sr2.get());21     System.out.println(sr3 + "---" + sr3.get());22     System.out.println(sr4 + "---" + sr4.get());23     System.out.println(sr5 + "---" + sr5.get());24         25     System.out.println("**********引用队列中的SoftReference展示**********");26     System.out.println(referenceQueue.poll());27     System.out.println(referenceQueue.poll());28     System.out.println(referenceQueue.poll());29     System.out.println(referenceQueue.poll());30     System.out.println(referenceQueue.poll());31     System.out.println(referenceQueue.poll());32 }</byte></byte></byte></byte></byte></byte></byte></byte></byte></byte></byte></byte></byte></byte>

运行结果为:

 1 **********引用队列测试********** 2  3 **********软引用关联的对象展示********** 4 java.lang.ref.SoftReference@50ed0a5---null 5 java.lang.ref.SoftReference@fa4033b---null 6 java.lang.ref.SoftReference@58d01e82---null 7 java.lang.ref.SoftReference@4783165b---[B@6f30d50a 8 java.lang.ref.SoftReference@6ef2bc8d---[B@23905e3 9 java.lang.ref.SoftReference@6db17b38---[B@1f10d1cb10 **********引用队列中的SoftReference展示**********11 java.lang.ref.SoftReference@50ed0a512 java.lang.ref.SoftReference@fa4033b13 java.lang.ref.SoftReference@58d01e8214 null15 null16 null

メモリ不足のため、最初の 3 つのソフト参照がリサイクルされ、リサイクルされた 3 つのソフト参照のメモリ アドレスがすべて一致していることがわかります。これは、上記のステートメントを証明しています。

以上がJVM の 4 つの参照状態の紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
Javaのプラットフォームの独立性を脅かしたり強化したりする新しいテクノロジーはありますか?Javaのプラットフォームの独立性を脅かしたり強化したりする新しいテクノロジーはありますか?Apr 24, 2025 am 12:11 AM

新しいテクノロジーは、両方の脅威をもたらし、Javaのプラットフォームの独立性を高めます。 1)Dockerなどのクラウドコンピューティングとコンテナ化テクノロジーは、Javaのプラットフォームの独立性を強化しますが、さまざまなクラウド環境に適応するために最適化する必要があります。 2)WebAssemblyは、Graalvmを介してJavaコードをコンパイルし、プラットフォームの独立性を拡張しますが、パフォーマンスのために他の言語と競合する必要があります。

JVMのさまざまな実装は何ですか、そしてそれらはすべて同じレベルのプラットフォームの独立性を提供しますか?JVMのさまざまな実装は何ですか、そしてそれらはすべて同じレベルのプラットフォームの独立性を提供しますか?Apr 24, 2025 am 12:10 AM

JVMの実装が異なると、プラットフォームの独立性が得られますが、パフォーマンスはわずかに異なります。 1。OracleHotspotとOpenJDKJVMは、プラットフォームの独立性で同様に機能しますが、OpenJDKは追加の構成が必要になる場合があります。 2。IBMJ9JVMは、特定のオペレーティングシステムで最適化を実行します。 3. Graalvmは複数の言語をサポートし、追加の構成が必要です。 4。AzulzingJVMには、特定のプラットフォーム調整が必要です。

プラットフォームの独立性は、開発コストと時間をどのように削減しますか?プラットフォームの独立性は、開発コストと時間をどのように削減しますか?Apr 24, 2025 am 12:08 AM

プラットフォームの独立性により、開発コストが削減され、複数のオペレーティングシステムで同じコードセットを実行することで開発時間を短縮します。具体的には、次のように表示されます。1。開発時間を短縮すると、1セットのコードのみが必要です。 2。メンテナンスコストを削減し、テストプロセスを統合します。 3.展開プロセスを簡素化するための迅速な反復とチームコラボレーション。

Javaのプラットフォームの独立性は、コードの再利用をどのように促進しますか?Javaのプラットフォームの独立性は、コードの再利用をどのように促進しますか?Apr 24, 2025 am 12:05 AM

java'splatformentedencefacilitatesecodereusebyAllowingbyTeCodeCodeCodeCodeTorunonAnyPlatformm.1)DevelopersConcodeCodeOnceOnceOnconconsentEntentEntEntEntEntEntentPlatforms.2)維持化されたアスカデドは、NoeedReadedoesではありません

Javaアプリケーションのプラットフォーム固有の問題をどのようにトラブルシューティングしますか?Javaアプリケーションのプラットフォーム固有の問題をどのようにトラブルシューティングしますか?Apr 24, 2025 am 12:04 AM

Javaアプリケーションのプラットフォーム固有の問題を解決するには、次の手順を実行できます。1。Javaのシステムクラスを使用して、システムプロパティを表示して実行中の環境を理解します。 2。ファイルクラスまたはjava.nio.fileパッケージを使用して、ファイルパスを処理します。 3。オペレーティングシステムの条件に応じてローカルライブラリをロードします。 4. VisualVMまたはJProfilerを使用して、クロスプラットフォームのパフォーマンスを最適化します。 5.テスト環境が、Dockerコンテナ化を通じて生産環境と一致していることを確認してください。 6. githubactionsを使用して、複数のプラットフォームで自動テストを実行します。これらの方法は、Javaアプリケーションでプラットフォーム固有の問題を効果的に解決するのに役立ちます。

JVMのクラスローダーサブシステムは、プラットフォームの独立性にどのように貢献していますか?JVMのクラスローダーサブシステムは、プラットフォームの独立性にどのように貢献していますか?Apr 23, 2025 am 12:14 AM

クラスローダーは、統一されたクラスファイル形式、動的読み込み、親代表団モデル、プラットフォーム非依存バイトコードを通じて、さまざまなプラットフォーム上のJavaプログラムの一貫性と互換性を保証し、プラットフォームの独立性を実現します。

Javaコンパイラはプラットフォーム固有のコードを作成しますか?説明する。Javaコンパイラはプラットフォーム固有のコードを作成しますか?説明する。Apr 23, 2025 am 12:09 AM

Javaコンパイラによって生成されたコードはプラットフォームに依存しませんが、最終的に実行されるコードはプラットフォーム固有です。 1。Javaソースコードは、プラットフォームに依存しないバイトコードにコンパイルされます。 2。JVMは、特定のプラットフォームのバイトコードをマシンコードに変換し、クロスプラットフォーム操作を保証しますが、パフォーマンスは異なる場合があります。

JVMは、さまざまなオペレーティングシステムでマルチスレッドをどのように処理しますか?JVMは、さまざまなオペレーティングシステムでマルチスレッドをどのように処理しますか?Apr 23, 2025 am 12:07 AM

マルチスレッドは、プログラムの応答性とリソースの利用を改善し、複雑な同時タスクを処理できるため、最新のプログラミングで重要です。 JVMは、スレッドマッピング、スケジューリングメカニズム、同期ロックメカニズムを介して、異なるオペレーティングシステム上のマルチスレッドの一貫性と効率を保証します。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

mPDF

mPDF

mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

AtomエディタMac版ダウンロード

AtomエディタMac版ダウンロード

最も人気のあるオープンソースエディター

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境