在生产环境下,通常都需要对JVM进行参数优化,其中对垃圾回收器的参数优化是一个非常重要的一方面。下面重点介绍Java的堆内存,垃圾回收算法,常用的垃圾回收器以及Java堆内存的分配策略,这些内容将作为对JVM进行垃圾回收参数优化的重要基础。然后通过简单示例验证Java的垃圾回收机制。
【Java堆内存结构】
Java的堆(Heap)是存放对象的内存区域。在逻辑上我们可以把堆细分为新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation)。
1. 新生代:可以再划分为Eden(伊甸)、From Survivor(存活者)和To Survivor三个逻辑区域, 对象优先存放在新生代的Eden区域。
2. 老年代:新生代的对象经过几次垃圾回收之后,仍然存活的将存放到老年代,并且大对象可以不经过新生代而直接存放在老年代。
3. 永久代:方法区使用永久代作为存储区域,在逻辑上,永久代是Java堆的一部分、但通常称之为“非堆”(Non-Heap)内存以示区别。方法区(Method Area)通常用来存放类的相关信息 (类加载器所加载的类的字段、方法签名等)、运行时常量池(如字符串常量池)、静态引用变量等。
Java的堆内存结构可下图简单描述,其中Eden、From Survivor和To Survivor区域这三部分将构成堆内存中的新生堆区域。
【对象是否存活】
在进行垃圾回收(Garbage Collection,GC)之前,需要判断堆中哪些对象是可回收的(不再被引用的)、哪些对象是不能被回收的。在面向对象的语言中,通常使用如下两种方式来进行对象是否存活的判断。
1. 引用计数法:Reference Counting
可以给每个对象添加引用计数器,对象有新的引用时、计数器+1操作,引用失效时、计数器-1操作,计数器的值为0时、该对象就是可回收的。Python语言的垃圾回收机制就采用引用计数法,但是这种方法很难解决对象的循环引用问题。
2. 根搜索算法:GC Roots Tracing
如果对象到GC Roots(比如,线程栈中的对象、静态引用变量等就可作为GC Roots)之间有引用链相连,表示该对象仍然被使用着的、不能被回收的,否则即认为对象没有被引用、是可以进行回收的。典型的高级语言如Java、C#都采用该方法。为了说明Java语言确实是采用根搜索算法判断对象是否存活的,编写程序:
public class CircularRefTest { private CircularRefTest instance = null; private byte[] buffer = new byte[1024 * 1024]; public static void main(String[] args) { CircularRefTest a = new CircularRefTest(); CircularRefTest b = new CircularRefTest(); a.instance = b; b.instance = a; a = null; b = null; System.gc(); } }
设置该程序运行时的VM Arguments参数:
-Xms3m -Xmx3m -XX:+PrintGCDetails
运行该程序,可看到控制台输出内容:
2014-09-13T16:07:35.998+0800: [GC [DefNew: 623K->64K(960K), 0.0028993 secs][Tenured: 1407K->1471K(2048K), 0.0045221 secs] 1647K->1471K(3008K), [Perm : 1732K->1732K(12288K)], 0.0075367 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2014-09-13T16:07:35.998+0800: [Full GC (System) [Tenured: 2495K->446K(3484K), 0.0050437 secs] 2536K->446K(4636K), [Perm : 1734K->1734K(12288K)], 0.0051196 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] Heap def new generation total 1664K, used 15K [0x02a50000, 0x02c10000, 0x02c50000) eden space 1536K, 1% used [0x02a50000, 0x02a53dd8, 0x02bd0000) from space 128K, 0% used [0x02bd0000, 0x02bd0000, 0x02bf0000) to space 128K, 0% used [0x02bf0000, 0x02bf0000, 0x02c10000) tenured generation total 3484K, used 446K [0x02c50000, 0x02fb7000, 0x03050000) the space 3484K, 12% used [0x02c50000, 0x02cbf9d0, 0x02cbfa00, 0x02fb7000) compacting perm gen total 12288K, used 1739K [0x03050000, 0x03c50000, 0x07050000) the space 12288K, 14% used [0x03050000, 0x03202e80, 0x03203000, 0x03c50000) No shared spaces configured.
说明:在该程序中,先后定义了两个对象,并且每个对象先后被引用了两次,然后每个对象其中的一个引用失效,如果采用引用计数法,这两个对象是不能被回收的,因为每个对象都还有一个未失效的引用。但是通过控制台的观察发现,这两个对象确实是被回收了的,这说明Java并未采用引用计数法。在上述程序中,引用变量a和b是线程栈引用变量,都可以作为GC Roots,a和b先后被置为null,这意味着对象通过instance引用无法和GC Roots建立一个有效的引用链,因此这两个对象都被回收了。这说明Java确实是采用根搜索算法来判断对象是否可回收的。
【引用类型的扩展】
强引用(Strong):传统意义的引用。
软引用(soft):在内存紧张时、会回收软引用对象(结合使用SoftReference类)。
弱引用:对象只能生存到下一次垃圾回收之前。
虚引用:引用关系最弱、无法通过虚引用获取对象。
public class SoftRefTest { private byte[] buffer = new byte[2 * 1024 * 1024]; public static void main(String[] args) { SoftRefTest objA = new SoftRefTest(); SoftReference<SoftRefTest> softRef = new SoftReference<SoftRefTest>(objA); objA = null; SoftRefTest objB = new SoftRefTest(); //System.gc(); } }
设置VM Arguments参数:
-Xms3m -Xmx3m -XX:+PrintGCDetails -XX:+PrintGCDateStamps
运行该程序,通过控制台观察到软引用对象objA确实被回收了。
【关于finalize()方法】
如果堆中的对象到GC Roots之间没有任何引用链,GC就可以对其进行回收. 在回收之前会调用对象的finalize()方法,可以通过覆盖该方法、把当前对象的引用重新和GC Roots连接起来、以阻止GC进行回收。 需要注意的是,一个对象的finalize()方法只会被执行一次、如果GC再次回收该对象,无法阻止被GC回收。
【永久代的垃圾回收】
在Sun公司的HotSpot虚拟机中,方法区存放在Java堆的永久代(Permanent Generation)。在大量涉及反射、动态代理、cglib等字节码(bytecode)技术的场景(如项目中使用Spring、Hibernate等框架),需要虚拟机具有类卸载的功能,保证永久代不会溢出。
【垃圾收集算法】
1. 复制算法:Copying
将堆内存划分为两块,当其中一块正在使用中的的内存空间紧张时、把其中“存活”(仍然被引用)着的对象复制到另外一块空闲着的内存区域,然后清空当前内存空间. 复制算法通常作为新生代的垃圾回收策略。
2. 标记-清除算法:Mark-Sweep
先标记出可回收的对象,然后进行统一清除. 缺点:效率低、并且产生大量不连续的内存碎片。
3. 标记-整理算法:Mark-Compact
标记出可回收的对象、将所有存活的对象向其中一端移动,然后直接清理掉另一端的内存区域。
4. 分代收集算法:Generational Collection
将Java堆划分为新生代、老年代,新生代中的大多数对象都是可回收的,而老年代中的对象大多数都是不可回收的 。新生代采用复制算法:大多数对象都是可回收的、只需复制少数存活的对象、回收效率较高。老年代只有少数对象可回收、标记效率较高,因此采用标记-清除(无须移动对象)、标记-整理(移动存活对象到其中一侧)算法相结合进行回收。
【垃圾收集器】
1. Serial收集器:串行收集器(collector)
单线程的垃圾收集器,是JVM运行在client模式下的默认收集器,进行垃圾回收时、必须暂停其他所有的工作线程(Sun称之为“Stop The World”)。
2. ParNew收集器:并行收集器
Serial收集器的多线程版本、多条线程并行进行垃圾回收、以减少暂停时间,通常用于JVM在server模式下新生代的收集器。并行:(Parallel):多个垃圾回收线程并行工作、仍需暂停其他工作线程。
3. CMS收集器:Concurrent Mark Sweep
并发标记清除收集器,通常作为老年代的收集器。并发(Concurrent):多条垃圾回收线程和工作线程交替运行、无须暂停工作线程,最大程度的提高垃圾效率、减少工作线程的停顿时间。
【堆内存分配策略】
1. 新创建的对象将存放在新生代的Eden(伊甸)区域、以及其中一个Survivor(存活者)区域(From Survivor)。
2. 堆内存紧张时、进行新生代对象的回收,存活着的对象将从Eden和From Survivor区域复制到To Survivor区域,如果To Survivor区域内存紧张、一部分存活对象将直接复制到老年代存放,然后清空Eden和From Survivor区域.。在下一次新生代垃圾回收时、From Survivor和To Survivor区域的角色互换.
3. 大对象(通常是指内容很长的字符串或者数组)直接放入老年代、以避免大对象在新生代的反复拷贝。
4. (新生代中)长期存活的对象将放入老年代,新生代中的对象每在Survivor区域完成一次拷贝、该对象的
年龄(Age)加1,当对象的年龄增加到一定值(默认为15)时、该对象将被存放到老年代,以避免该对象在
新生代的反复拷贝。
【Minor/Major GC】
新生代GC(Minor GC):新生代的垃圾回收非常频繁(尽可能快的释放出可用空间)、效率很高(采用复制算法,大多数对象可回收、只需复制少数存活对象)。
老年代GC(Major/Full GC):老年代的垃圾回收、效率通常比新生代的Minor GC慢至少10倍,(采用标记-清除、标记-整理算法),每次Full GC会同时进行至少一次Minor GC, 通常在堆内存紧张、或者显示的调用System.gc()时触发Full GC。
=================================
垃圾回收机制的学习,确实枯燥乏味,但这却是进行JVM参数调优的重要基础!
相关推荐
例如,“-Xms”和“-Xmx”分别用于设置JVM的初始堆内存和最大堆内存,“-Xss”用于设置线程堆栈大小,“-Xmn”用于设置年轻代内存大小。而“-XX”参数中,“-XX:MaxPermSize”用于设置方法区的最大内存大小。 除了...
在深入探讨JVM(Java虚拟机)的理论与实践结合时,我们首先需要理解JVM在Java编程中的核心地位。JVM是Java平台的核心组成部分,它负责执行编译后的Java字节码,使得Java程序具备跨平台的能力。在这个解密JVM-day04的...
JVM提供了内存管理、类加载、字节码解释执行以及垃圾回收等功能,使得Java程序具有高效、安全和可移植性。 二、JVM结构 JVM主要由以下几个部分构成: 1. 类加载器:负责加载类文件到JVM内存中。 2. 运行时数据区:...
其中,类加载机制保证了程序的动态性和安全性,执行引擎负责解释和执行字节码,内存管理则涉及到堆和栈的分配、对象的创建与销毁,而垃圾回收则自动化地处理不再使用的对象,避免内存泄漏。 深入理解JVM对于开发高...
总结,JVM调优是一项技术性极强的工作,需要深入理解JVM内存结构、垃圾回收机制,结合实际应用情况,通过调整相关参数实现最佳性能。实践中应结合理论知识与实践经验,不断测试、监控、分析,确保应用的高效稳定运行...
总的来说,这个资料包涵盖了Java内存分配的理论基础、垃圾回收的机制、性能调优的方法以及诊断和解决内存问题的实践技巧。通过学习这些内容,你可以提升对Java内存管理的理解,进而编写出更高效、更稳定的Java程序。
1. JVM内存结构 在JVM内存模型中,堆内存主要用于存储对象实例和数组。它被所有线程共享,由Java虚拟机自动管理,分为新生代、老年代和永久代(Java 8后改为元空间)三个主要部分。 2. 新生代与老年代 新生代...
调优方法包括使用监控工具跟踪JVM性能,识别性能瓶颈,然后调整相关参数或内存结构来优化性能。实践中,可能需要反复测试和调整以达到最佳效果。 9. JVM内存管理 JVM内存管理涵盖了内存区域划分、垃圾回收机制、...
标签“jvm java 虚拟机”直接明确了文档的主题范围,涵盖JVM的基础知识、Java程序的内存结构以及Java编程语言与JVM之间的关系。 在提供的部分内容中,我们看到了一个结构化的目录,这表明文档系统地覆盖了JVM相关的...
垃圾回收是JVM自动管理内存的重要机制之一。通过垃圾回收机制,JVM能够自动识别不再使用的对象,并释放其占用的内存空间。了解不同的垃圾回收算法(如标记-清除算法、复制算法、标记-整理算法等)及其适用场景对于...
【JVM调优视频理论及工具】主要涵盖了Java虚拟机(JVM)的优化实践与相关的分析工具。在Java开发中,JVM调优是提升应用程序性能的关键环节,尤其是在高并发、大数据处理等场景下,良好的JVM配置能显著提高系统效率。...
垃圾收集机制(GC)是JVM的重要组成部分,其主要功能是自动回收不再使用的对象占用的内存空间,避免内存泄漏等问题。GC主要分为以下几个方面: - **分代收集理论**:根据对象存活周期的不同将堆空间划分为新生代和...
JVM(Java Virtual Machine,Java虚拟机)...冯立全通过分享,将JVM原理的理论与实践相结合,为听众提供了一个全面了解JVM的机会。这对于需要深入探讨Java生态系统和提高Java开发技能的开发者来说,是非常宝贵的资源。
《深入理解JVM》(Inside the JVM) 是一本关于Java虚拟机的重要参考资料,它涵盖了JVM的内部工作...通过提供的源代码和演示,学习者可以将理论与实践相结合,增强对JVM的理解,这对于Java开发人员来说是极其宝贵的资源。
相关参数如堆内存大小、新生代和老年代的比例、垃圾回收策略等,都是性能调优时需要关注的。 JVM工具方面,常用的有jps、jstat、jmap、jstack、jconsole等。这些工具可以帮助开发者监控和分析JVM的状态和性能问题。...
《深入理解JVM CHM》是一本专注于Java虚拟机(JVM)的深入解析书籍,其主要内容涵盖了JVM的工作原理、内存管理、垃圾收集、性能优化等多个关键领域。结合提供的标签"源码"和"工具",我们可以推断这本书不仅探讨了...
本教程将带你探索JVM的奥秘,从内存管理到垃圾回收,从编译优化到故障排查,全方位解析JVM的工作机制。 一、JVM结构与原理 1. 类装载子系统:负责加载、验证、解析和初始化类文件。 2. 运行时数据区:包括堆、方法...
当对象不再被引用时,JVM会自动回收这些对象占用的内存空间,这一过程称为**垃圾回收**。常见的垃圾回收算法包括标记-清除算法、复制算法、标记-整理算法以及分代收集算法等。不同版本的JVM采用不同的垃圾收集器,如...
- **对象引用关系图**:通过图形化展示对象间的引用关系,帮助理解内存结构。 - **类统计与分析**:统计不同类的实例数量和内存占用,找出占用内存较大的类。 4. **使用HeapAnalyzer的步骤** - **生成heapdump**...
"itcast-jvm.zip"中的示例可能包含不同GC策略的演示,如新生代GC(Minor GC)、老年代GC(Major GC)以及Full GC,理解这些策略有助于优化内存分配和垃圾回收效率。 三、JVM参数调优 1. **堆内存大小**:通过-Xms...