Java虚拟机以及垃圾回收总结

Java虚拟机主要由三大部分组成:类加载器、运行时数据区和执行引擎

运行时数据区:由方法区、、Java虚拟机栈、本地方法栈和程序计数器组成。
运行时常量池数据:保存在方法区中。
执行引擎:由即时编译器(JIT Compiler)和垃圾收集器组成。

方法区空间为所有Java虚拟机线程共享,存放编译后的代码:

The method area is analogous to the storage area for compiled code of a conventional language or analogous to the "text" segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods used in class and instance initialization and interface initialization.

垃圾回收

使用C语言开发时,需要开发者手动分配和回收内存,在Java中则是通过垃圾收集器自动处理。

在早期一段时间,JVM是通过标记和整理所有对象来进行垃圾回收,不仅效率低下且对象列表的逐步增长会导致垃圾回收时间越来越长。后来通过研究发现,应用程序中大部分对象的存活期都是较短的,前期会有大部分对象存活,但随着时间的推移会越来越少。通过对这种现象的发现和研究,对堆进行分代的划分:分为年轻代,老年代和永久代

JDK1.7堆结构

Java虚拟机以及垃圾回收总结_第1张图片
JDK1.7 Java Hotspot 堆结构

JDK1.8默认的堆结构
Java虚拟机以及垃圾回收总结_第2张图片
JDK1.8 Java Hotspot 默认堆结构

年轻代又由一个eden区和两个survivor区构成,首先新对象会分配在eden区,当eden区分配满时会触发轻量级垃圾回收(minor garbage collection),将eden区中存活的对象移动到第一个survivor区(S0),并且清空eden区,随后继续分配新对象到eden区;当再次触发垃圾回收时,则会将eden区和S0中存活的对象移动到另一个survivor区(S1),然后清空eden区和S0,你可以将两个survivor区看成临时交换区,其中一个始终保持为空。存活的对象每经历一次垃圾回收可以看成增长一岁,当增长足够岁数后会移到老年代。[1]

当老年代分配满时,则会触发重大级别的垃圾回收(Major garbage collection),Major GC会需要更长的时间,因为它对整个堆进行垃圾回收,涉及所有存活的对象。无论是Minor还是Major GC都是会导致应用暂停的事件(Stop the World Event): 应用的所有线程都会挂起直到垃圾回收结束。所以对于需要即时响的应用程序,要尽可能减少Major GC事件的发生。

System.gc() 和 Runtime.getRuntime().gc() 方法在JDK1.6 Hotspot JVM的实现是暂停应用程序所有线程并对整个堆进行垃圾回收的,所以不要一般情况下不要在代码中调用这两个方法,交由JVM自己进行垃圾回收,如有调用了,可以设置并发收集: -XX:+ExplicitGCInvokesConcurrent[2]

永久代则是JVM存放描述所需类和方法的元数据的区间,另外Java 类库中的类和方法也是存放在这里。在JDK1.8中,PermGen即永久代已经被移除了。[3]

在官方Java虚拟机调优文档[4]最后,有提到:

    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 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.


垃圾收集器

串行收集器

如果应用程序只有很少的数据集时,可以使用串行收集器:-XX:+UseSerialGC

并行收集器

Java虚拟机以及垃圾回收总结_第3张图片
使用并行收集器时的堆结构

在server VM虚拟机上面,并行收集器是默认的垃圾回收器。 可以使用 -XX:+UseParallelGC 来启用,另外可以使用 -XX:-UseParallelOldGC来关闭并行压缩功能。
使用并行收集器时,你可以指定垃圾回收的最大中断时间、吞吐量以及寻址空间;
指定最大中断时间: 默认是没有最大中断时间的限制的,可以通过参数 -XX:MaxGCPauseMillis=设定,然后JVM会调整垃圾回收的相关设定,比如堆大小等参数来使得垃圾回收中断时间小于设定的值,这个最大中断时间只是设定一个目标,不是一定能够达成。
吞吐量: 吞吐量则是应用正常运行时间和垃圾回收时间的比例,可通过 参数 -XX:GCTimeRatio=来设定,比如设置 -XX:GCTimeRatio=19说明有5%的时间是用来垃圾回收的, -XX:GCTimeRatio=99则表示只允许1%的时间用着垃圾回收上,这个参数的默认值是99.
内存空间:可以通过 -Xmx来指定最大堆空间,另外,并行收集器还有个隐含的目标,就是在能达成以上两个目标的情况下最小化堆空间。
由于并行收集器需要调整堆分代空间,这就是使用它时堆结构与默认不同的原因了。

并发标记清理收集器(CMS: Concurrent Mark Sweep)

使用参数 -XX:+UseConcMarkSweepGC来设置使用CMS收集器,
CMS收集器可以在应用程序线程执行的同时,并发使用多个线程来追踪存活的对象以及进行内存整理。

垃圾优先收集器(G1: Garbage-First)

Java虚拟机以及垃圾回收总结_第4张图片
G1垃圾收集器的堆结构

使用参数 -XX:+UseG1GC来启用G1垃圾收集器,它的设计意图是满足更小GC中断时间的需求,将堆内存平均分割为固定大小的区间,是逻辑意义上面的分代,用不同空区间集合表示不同代,另外占用超过一半区间的对象被视为极大对象,会放置在多个区间合并起来的空间。

JVM配置[5]

查看当前JVM配置:
java -XX:+PrintCommandLineFlags -version

常用参数:

-Xmnsize or -XX: NewSize·: 设置年轻代大小, 示例: -Xmn512m
jdk 1.7: Sets the size of the young generation (nursery).
jdk 1.8: Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery). Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, g or G to indicate gigabytes.

-Xmssize: 设置堆初始大小,示例:-Xms1g
Sets the initial size (in bytes) of the heap. This value must be a multiple of 1024 and greater than 1 MB. Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, g or G to indicate gigabytes.

-Xmxsize: 设置可分配内存池最大值
Specifies the maximum size (in bytes) of the memory allocation pool in bytes. This value must be a multiple of 1024 and greater than 2 MB. For server deployments, -Xms and -Xmx are often set to the same value.

-Xsssize: 设置线程栈大小
Sets the thread stack size (in bytes). Append the letter k or K to indicate KB, m or M to indicate MB, g or G to indicate GB. The default value depends on virtual memory.

-XX:+PrintCommandLineFlags
Enables printing of ergonomically selected JVM flags that appeared on the command line. It can be useful to know the ergonomic values set by the JVM, such as the heap space size and the selected garbage collector. By default, this option is disabled and flags are not printed.

-XX:InitialHeapSize=size

-XX:InitialSurvivorRatio=ratio

Hotspot JVM常见问题:

Frequently Asked Questions About the Java HotSpot VM


  1. 配置对象最大可经历所少次垃圾回收才移到老年代的参数 -XX:MaxTenuringThreshold,这个参数的最大值是15,对于并行收集器,该参数默认值是最大值15,对于CMS收集器,该参数值默认值是6. ↩

  2. Concurrent Mark Sweep Collector Enhancements ↩

  3. JEP 122: Remove the Permanent Generation ↩

  4. JVM Garbage Collection Tuning Guide ↩

  5. JDK1.8 JVM参数配置项 ↩

你可能感兴趣的