java文档 第十一章 其他考量-b

Finalization 和弱引用、软引用、虚引用

Finalization and Weak, Soft, and Phantom References

Some applications interact with garbage collection by using finalization and weak, soft, or phantom references. These features can create performance artifacts at the Java programming language level. An example of this is relying on finalization to close file descriptors, which makes an external resource (descriptors) dependent on garbage collection promptness. Relying on garbage collection to manage resources other than memory is almost always a bad idea.

某些应用程序使用finalization和弱引用、软引用、虚引用和gc交互。这些特性在Java编程语言层面可能造成一些性能问题。一个例子是使用finalization方法来关闭文件描述符,这种做法使外部资源描述符严重依赖于gc的性能。而依靠gc来管理资源,而不是依靠内存管理,从来都不是一个好方法。

比如如下的一个例子:

public class Image1 {

// pointer to the native image data

private int nativeImg;

private Point pos;

private Dimension dim;

// it disposes of the native image;

// successive calls to it will be ignored

private native void disposeNative();

public void dispose() { disposeNative(); }

protected void finalize() { dispose(); }

static private Image1 randomImg;

}

Some time after an Image1 instance has become unreachable, the Java virtual machine (JVM) will call its finalize() method to ensure that the native resource that holds the image data (pointed to by the integer nativeImg in the example) has been reclaimed. Notice, however, that the finalize() method, despite its special treatment by the JVM, is an arbitrary method that contains arbitrary code. In particular, it can access any field in the object (pos and dim in the example). Surprisingly, it can also make the object reachable again by, say, making it reachable from a static field (e.g., randomImg = this;).

当某一个Image1的实例变为不可达的时候,JVM将调用这个实例的finalize()方法来确保持有图像数据的本地资源被正确回收(就是上面的那个nativeImg)。然而,finalize()尽管是一个特殊的方法,可以被JVM调用,但同时finalize()也是一个普通方法,也可以被其他代码任意调用。并且finalize()方法可以访问类实例的任何对象和字段(比如上面例子的dim和pos)。令人吃惊的是,通过对静态字段赋值,它还能让对象变的重新可达(比如:randomImg = this)。

The following steps describe the lifetime of a finalizable object obj—that is, an object whose class has a non-trivial finalizer (see Figure 1):

下面描述了一个finalizable对象obj的生命周期

When obj is allocated, the JVM internally records that obj is finalizable (this typically slows down the otherwise fast allocation path that modern JVMs have).

当obj被分配了空间,JVM内部会记录下obj是一个可finalizable的对象。

 

When the garbage collector determines that obj is unreachable, it notices that obj is finalizable (as it had been recorded upon allocation) and adds it to the JVM's finalization queue. It also ensures that all objects reachable from obj are retained, even if they are otherwise unreachable, as they might be accessed by the finalizer. Figure 2 illustrates this for an instance of Image1.

当gc判定obj不可达,就会通知VM,obj是可以finalizable(就像上面在分配空间的时候记录的一样)的,并将obj加入到JVM的finalization队列。gc还确保所有obj指向的其他对象被保留,尽快从这些对象本身的角度来看,可能已经不可达了,这是因为在obj对象的finalize过程中,可能会被访问到。

At some point later, the JVM's finalizer thread will dequeue obj, call its finalize() method, and record that obj's finalizer has been called. At this point, obj is considered to be finalized.

在稍后的某一个时刻,JVM的finalizer线程开始从队列中弹出obj,并调用obj对象的finalize()方法,并记录obj对象的finalizer已经被调用了。在这个时刻,obj就被认为是已经finalized了。

 

When the garbage collector rediscovers that obj is unreachable, it will reclaim its space along with everything reachable from it (provided that the latter is otherwise unreachable).

当gc再次发现obj是不可达的,将会回收obj的空间,和obj引用的其他对象(这些被引用的对象自身也必须是不可达的)

Notice that the garbage collector needs a minimum of two cycles (maybe more) to reclaim obj and needs to retain all other objects reachable from obj during this process. If a programmer is not careful, this can create temporary, subtle, and unpredictable resource-retention issues. Additionally, the JVM does not guarantee that it will call the finalizers of all the finalizable objects that have been allocated; it might exit before the garbage collector discovers some of them to be unreachable.

注意,gc需要至少两个周期(也许更多)来回收obj对象,并且在这个期间需要保存所有和gc相关的其他对象可达(以防止obj的finalize()方法中用到)。如果编程者不注意,那么这就会造成短暂的、微妙的,不可预测的资源保持问题。并且JVM并不保证一定能调用到可finalizable对象的finalize方法。有可能在gc发现这些对象的时候,JVM就退出了。

 

Explicit Garbage Collection外部gc

Another way that applications can interact with garbage collection is by invoking full garbage collections explicitly by calling System.gc(). This can force a major collection to be done when it may not be necessary (for example, when a minor collection would suffice), and so in general should be avoided. The performance effect of explicit garbage collections can be measured by disabling them using the flag -XX:+DisableExplicitGC, which causes the VM to ignore calls to System.gc().

另外一个应用程序和gc交互的方法是通过调用System.gc()来触发全垃圾回收。这种调用方式可能在不必要的情况下触发major gc(比如当前仅有一个minor gc就足够了),所以总体上来说应该避免这种调用。使用-XX:+DisableExplicitGC命令行参数,可以让VM忽略System.gc()的调用。

 

One of the most commonly encountered uses of explicit garbage collection occurs with the distributed garbage collection (DGC) of Remote Method Invocation (RMI). Applications using RMI refer to objects in other virtual machines. Garbage cannot be collected in these distributed applications without occasionally invoking garbage collection of the local heap, so RMI forces full collections periodically. The frequency of these collections can be controlled with properties, as in the following example:

最常见的使用外部gc的场景发生在RMI的DGC。应用程序使用RMI在VM中引用远程对象。如果不在本地堆周期性的调用gc,这些分布式对象根本不会释放,所以RMI强制要求周期性的full gc。可以使用如下属性来控制调用gc的时间间隔

 

java -Dsun.rmi.dgc.client.gcInterval=3600000

-Dsun.rmi.dgc.server.gcInterval=3600000

 

This example specifies explicit garbage collection once per hour instead of the default rate of once per minute. However, this may also cause some objects to take much longer to be reclaimed. These properties can be set as high as Long.MAX_VALUE to make the time between explicit collections effectively infinite if there is no desire for an upper bound on the timeliness of DGC activity.

上面的例子规定了每个小时gc一次,而不是默认的每分钟。然而这样也会造成对象占用空间时间过长。

Soft References软引用

Soft references are kept alive longer in the server virtual machine than in the client. The rate of clearing can be controlled with the command-line option -XX:SoftRefLRUPolicyMSPerMB=<N>, which specifies the number of milliseconds (ms) a soft reference will be kept alive (once it is no longer strongly reachable) for each megabyte of free space in the heap. The default value is 1000 ms per megabyte, which means that a soft reference will survive (after the last strong reference to the object has been collected) for 1 second for each megabyte of free space in the heap. This is an approximate figure because soft references are cleared only during garbage collection, which may occur sporadically.

软引用在服务器级VM中比客户端级VM保存的时间要长一些。参数-XX:SoftRefLRUPolicyMSPerMB可以控制一个软引用保存的毫秒数(强引用不再可达之后),对于堆的每1M剩余空间。默认值是1000毫秒每M,这意味着一个软引用将会生存1秒钟,相对于每1M堆的剩余空间。这只是一个近似值。

 

Class Metadata类的数据

Java classes have an internal representation within Java Hotspot VM and are referred to as class metadata. In previous releases of Java Hotspot VM, the class metadata was allocated in the so called permanent generation. In JDK 8, the permanent generation was removed and the class metadata is allocated in native memory. The amount of native memory that can be used for class metadata is by default unlimited. Use the option MaxMetaspaceSize to put an upper limit on the amount of native memory used for class metadata.

Java类在JavaVM之内有一个内部的表示形态,被称为类的元数据。在之前JVM版本中,类的元数据被分配在永久代。在JDK8之后,永久代被取消了,类的元数据被分配到本地内存之中。本地内存的大小默认是没有限制的。使用选项MaxMetaspaceSize来为存储Java类元数据的本地内存设定一个上限。

 

Java Hotspot VM explicitly manages the space used for metadata. Space is requested from the OS and then divided into chunks. A class loader allocates space for metadata from its chunks (a chunk is bound to a specific class loader). When classes are unloaded for a class loader, its chunks are recycled for reuse or returned to the OS. Metadata uses space allocated by mmap, not by malloc.

JavaVM为元数据显示管理使用的空间。空间向操作系统申请,并划分为块。类加载器负责为元数据从块上分配空间。当类被类加载器卸载的时候,块空间被回收以便重新使用或者归还给操作系统。元数据分配的空间是使用mmap函数分配的,而不是malloc。

 

If UseCompressedOops is turned on and UseCompressedClassesPointers is used, then two logically different areas of native memory are used for class metadata. UseCompressedClassPointers uses a 32-bit offset to represent the class pointer in a 64-bit process as does UseCompressedOops for Java object references. A region is allocated for these compressed class pointers (the 32-bit offsets). The size of the region can be set with CompressedClassSpaceSize and is 1 gigabyte (GB) by default. The space for the compressed class pointers is reserved as space allocated by mmap at initialization and committed as needed. The MaxMetaspaceSize applies to the sum of the committed compressed class space and the space for the other class metadata.

如果UseCompressedOops开关打开了,并且使用了UseCompressedClassesPointers属性,那么会有类的元数据将存储在两个逻辑上不相同的本地内存区域。UseCompressedClassPointers使用32bit偏移量来代表类指针,UseCompressedOops使用64bit地址来代表类指针。这些压缩的类指针被存储在一个区域,这个区域可以使用参数CompressedClassSpaceSize设定,默认大小是1GB。保存类指针的空间是在使用mmap分配空间的时候完成初始化的,根据需要来提交使用。

 

Class metadata is deallocated when the corresponding Java class is unloaded. Java classes are unloaded as a result of garbage collection, and garbage collections may be induced in order to unload classes and deallocate class metadata. When the space committed for class metadata reaches a certain level (a high-water mark), a garbage collection is induced. After the garbage collection, the high-water mark may be raised or lowered depending on the amount of space freed from class metadata. The high-water mark would be raised so as not to induce another garbage collection too soon. The high-water mark is initially set to the value of the command-line option MetaspaceSize. It is raised or lowered based on the options MaxMetaspaceFreeRatio and MinMetaspaceFreeRatio. If the committed space available for class metadata as a percentage of the total committed space for class metadata is greater than MaxMetaspaceFreeRatio, then the high-water mark will be lowered. If it is less than MinMetaspaceFreeRatio, then the high-water mark will be raised.

类元数据在对应的Java类被卸载的时候,被释放。Java类的卸载是gc的结果,gc的过程包含卸载类和释放类元数据占用的空间。当类的元数据的空间占用达到一定程度(a high-water mark,高水位标记)的时候,gc就会包含卸载类的过程。在gc之后,high-water标记随着释放空间的量而浮动。high-water标记可以提升数值,以防止过快发生gc。High-water标记初始化的值由MetaspaceSize参数设定。提升和降低的比率由参数MaxMetaspaceFreeRatio和MinMetaspaceFreeRatio来控制。如果提交的空间使用占比比MaxMetaspaceFreeRatio要大,那么high-water要提高阈值,反之,要降低阈值。

 

Specify a higher value for the option MetaspaceSize to avoid early garbage collections induced for class metadata. The amount of class metadata allocated for an application is application-dependent and general guidelines do not exist for the selection of MetaspaceSize. The default size of MetaspaceSize is platform-dependent and ranges from 12 MB to about 20 MB.

为MetaspaceSize设定一个较高的值来避免过早的GC。各平台和应用场景不同,互相独立。默认值是12MB-20MB。

 

Information about the space used for metadata is included in a printout of the heap. A typical output is shown in Example 11-1, "Typical Heap Printout".

下图是元数据占用空间的打印信息

Example 11-1 Typical Heap Printout


 

 

第十二章 附录:gc参数

GC配置参数表

 

-XX:MaxGCPauseMillis=<nnn>

最大gc暂停时间,毫秒

 

-XX:GCTimeRatio=<nnn>

吞吐量,最大gc占用时间比,整数,规定的是分母,如GCTimeRatio=19,意味着gc的时间是1/20

 

-verbose:gc

打印gc明细

 

-XX:+PrintGCDetails

打印gc明细

 

-XX:+PrintGCTimeStamps

打印gc时间戳(在gc日志的行首)

 

-XX:MinHeapFreeRatio=<minimum>

最小堆空闲内存剩余比例,默认是40,整数

 

-XX:MaxHeapFreeRatio=<maximum>

最大堆空闲内存剩余比例,默认是70,整数

 

-Xms<min>

最小堆内存绝对值,要带单位,比如-Xms256M

 

-Xmx<max>

最大堆内存绝对值,要带单位,比如-Xmx512M

 

-XX:NewRatio=<num>

新生代区域占总内存区域百分比,比如-XX:NewRatio=2,那么意味着新生代和老生代的比率是1:2

 

-XX:NewSize=<num unit>

新生代区域的内存绝对值初始大小,比如-XX:NewSize=1310M

 

-XX:MaxNewSize=<num unit>

新生代区域的内存绝对值最大大小,比如-XX:MaxNewSize=1310M

 

-XX:SurvivorRatio=<num>

新生代内eden区和survivor区大小的比例,比如-XX:SurvivorRatio=6,survivor占八分之一

 

-XX:+PrintTenuringDistribution

打印新生代survivor区半空的阀值

 

-XX:+PrintFlagsFinal

打印gc heap的默认初始值和最大值

 

-XX:+UseSerialGC

使用顺序gc进行垃圾回收

 

-XX:+UseParallelGC

使用并行gc进行垃圾回收

 

-XX:-UseParallelOldGC

关闭并行gc时,major gc使用的并发压缩模式;默认这个模式,在规定了参数-XX:+UseParallelGC时是自动打开的。在某些老旧程序上,关闭此模式可以获得更稳定的程序运行结果。

 

-XX:ParallelGCThreads=<N>

使用并行gc时,设定并行gc使用的线程数

 

-XX:+UseConcMarkSweepGC

使用CMS多数并行gc,CMS更强调短的暂停时间目标

 

-XX:CMSInitiatingOccupancyFraction=<N>

设定CMS的启动初始阀值,当老生代内存占用超过这个阀值的时候,CMS就会启动,默认值为92,区间为0-100

 

-XX:+UseG1GC

使用G1多数并行gc,G1更强调程序总体的吞吐量指标

 

-XX:InitiatingHeapOccupancyPercent=<NN>

设定初始堆占用百分比,默认值是45,当达到这个值规定的阀值时,会启动G1 GC的堆内存对象标记。

 

-XX:GCPauseIntervalMillis=<nnn>

设定GC暂停这个动作在多长时间跨度内才能发生一次,比如=200,意味着每200毫秒才能发生一次GC的暂停,如果设定为0,意味着系统可以随便暂停

 

-XX:-UseGCOverheadLimit

使用此选项来禁用当gc时间过长时(比如98%的时间都用在gc上了)VM抛出的OutOfMemoryError错误

 

-XX:YoungGenerationSizeIncrement=<Y>

新生代内存每次扩展比例,最终扩展的值为1/(1+Y)

 

-XX:TenuredGenerationSizeIncrement=<T>

老生代内存每次扩展比例,最终扩展的值为1/(1+T)

 

-XX:AdaptiveSizeDecrementScaleFactor=<D>

内存收缩比例,最终的比值为X/D,其中X为扩展的系数

 

-XX:+DisableExplicitGC

命令行参数,可以让VM忽略System.gc()的调用

 

感谢大神分享

posted on 2016-09-30 23:59  &#127774;Bob  阅读(223)  评论(0编辑  收藏  举报

导航