03 JVM的垃圾回收机制
1、前言
理解JVM的垃圾回收机制(简称GC)有什么好处呢?作为一名软件开发者,满足自己的好奇心将是一个很好的理由,不过更重要的是,理解GC工作机制可以帮助你写出更好的Java程序。
在学习GC前,你应该知道一个技术名词:“stop-the-world” ,无论你选择哪种GC算法,“stop-the-world”都会发生。“stop-the-world”意味着JVM停止应用程序,而去进行垃圾回收。当“stop-the-world”发生时,除了进行垃圾回收的线程,其他所有线程都将停止运行。被中断的任务将在GC任务完成后恢复执行。GC调优往往意味着减少“stop-the-world”的时间。
2、分代垃圾收集机制
在HotSpot虚拟机中,将内存分为 年轻代(young generation)、老年代(old generation) 和 永久代(permanent generation)
我们看一下这幅图:
年轻代: 新创建的对象都存放在这里。因为大多数对象很快变得不可达,所以大多数对象在年轻代中创建,然后消失。当对象从这块内存区域消失时,我们说发生了一次“minor GC”。
老年代: 没有变得不可达,存活下来的年轻代对象被复制到这里。这块内存区域一般大于年轻代。因为它更大的规模,GC发生的次数比在年轻代的少。对象从老年代消失时,我们说“major GC”(或“full GC”)发生了。
永久代: 永久代(permanent generation) 也称为“方法区(method area)”,它存储class对象和字符串常量。所以这块内存区域绝对不是永久的存放从老年代存活下来的对象的。发生在这里的垃圾回收也被称为major GC。
3、垃圾收集算法
垃圾收集算法如下:
- 年轻代-复制算法
- 老年代-标记清除算法
- 老年代-标记整理算法
- 永久代-方法区回收
年轻代-复制算法
该算法的核心是将可用内存按容量划分为大小相等的两块,每次只用其中一块,当这一块的内存用完,就将还存活的对象复制到另外一块上面,然后把已使用过的内存空间一次清理掉。这使得每次只对其中一块内存进行回收,分配也就不用考虑内存碎片等复杂情况,实现简单且运行高效。如下图:
老年代-标记清除算法
该算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象(可达性分析),在标记完成后统一清理掉所有被标记的对象。如下图:
该算法会有以下两个问题:
- 效率问题:标记过程和清除过程的效率都不高;
- 空间问题:标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致在运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集。
老年代-标记整理算法
标记清除算法会产生内存碎片,而复制算法需要有额外的内存担保空间,于是针对老年代的特点,又有了标记整理算法。标记整理算法的标记过程与标记清除算法相同,但后续步骤不再对可回收对象直接清理,而是让所有存活的对象都向一端移动,然后清理掉这一端边界以外的内存。如下图:
永久代-方法区回收
在方法区进行垃圾回收一般“性价比”较低,因为在方法区主要回收两部分内容:废弃常量 和 无用的类 。
回收废弃常量与回收其他年代中的对象类似。
但是判断一个类是不是无用的类的条件则相当苛刻:
- 该类所有的实例都已经被回收,Java堆中不存在该类的任何实例;
- 该类对应的Class对象没有在任何地方被引用;
- 加载该类的ClassLoader已经被回收;
但即使满足以上条件也未必一定会回收,Hotspot VM还提供了-Xnoclassgc参数控制(关闭CLASS的垃圾回收功能)。因此在大量使用动态代理、CGLib等字节码框架的应用中一定要关闭该选项,开启VM的类卸载功能,以保证方法区不会溢出。
4、垃圾回收的两个重要方法
System.gc()方法 和 finalize()方法
System.gc()方法
使用System.gc() 可以不管JVM使用的是哪一种垃圾回收的算法,都可以请求Java的垃圾回收。在命令行中有一个参数-verbosegc
可以查看Java使用的堆内存的情况,它的格式如下:java -verbosegc classfile
由于这种方法会影响系统性能,不推荐使用,所以不详细介绍。
finalize()方法
JVM垃圾回收器在回收一个对象之前,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了缺省机制来终止该对象从而释放资源,这个方法就是finalize() 。它的原型为:protected void finalize() throws Throwable
在finalize() 方法返回之后,对象消失,垃圾收集开始执行。原型中的throws Throwable 表示它可以抛出任何类型的异常。
之所以要使用finalize(),是因为存在着垃圾回收器不能处理的特殊情况。例如:
由于在分配内存的时候可能采用了类似 C语言的做法,而非Java通常的new 。这种情况主要发生在 native 方法中,比如 native 方法调用了
C/C++
的malloc()
函数来分配存储空间,除非调用 free() 函数,否则这些内存空间将不会得到释放,那么这个时候就可能造成内存泄漏。但是由于 free() 是C/C++
中的函数,所以 finalize() 中可以用本地方法来调用它。以释放这些“特殊”的内存空间。或者是打开的文件资源,这些资源不属于垃圾回收器的回收范围。
5、触发GC(Garbage Collector)的条件
GC在优先级最低的线程中运行,一般在应用程序空闲即没有应用线程在运行时被调用。但下面的条件例外。
Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制调用GC线程。若GC一次之后仍不能满足内存分配,JVM会再进行两次GC,若仍无法满足要求,则JVM将报“out of memory”的错误,Java应用将停止。
6、减少GC开销的措施
不要显式调用System.gc() 。此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。大大的影响系统性能。
尽量减少临时对象的使用。临时对象在跳出函数调用后,会成为垃圾,少用临时变量就相当于减少了垃圾的产生,从而延长了出现上述第二个触发条件出现的时间,减少了主GC的机会。
对象不用时最好显式置为Null。一般而言,为Null的对象都会被作为垃圾处理,所以将不用的对象显式地设为Null,有利于GC收集器判定垃圾,从而提高了GC的效率。
尽量使用StringBuffer,而不用String来累加字符串。由于String是固定长的字符串对象,累加String对象时,并非在一个String对象中扩增,而是重新创建新的String对象,如
Str5=Str1+Str2+Str3+Str4;
这条语句执行过程中会产生多个垃圾对象,因为每次作“+”操作时都必须创建新的String对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多的垃圾。避免这种情况可以改用StringBuffer来累加字符串,因StringBuffer是可变长的,它在原有基础上进行扩增,不会产生中间对象。能用基本类型如
int, long
就不用包装类型Integer, Long
。基本类型变量占用的内存资源比包装类型占用的少得多,如果没有必要,最好使用基本变量。尽量少用静态对象变量。静态变量属于全局变量,不会被GC回收,它们会一直占用内存。
分散对象创建或删除的时间。集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM在面临这种情况时,只能进行主GC,以回收内存或整合内存碎片,从而增加主GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会。
7、几种垃圾收集器
在JDK7中,有5种垃圾收集器:
- Serial收集器
- Parallel收集器
- Parallel Old收集器 (Parallel Compacting GC)收集器
- Concurrent Mark & Sweep GC (or “CMS”)收集器
- Garbage First (G1) 收集器
其中,Serial 收集器一定不能用于服务器端。这个收集器类型仅应用于单核CPU桌面电脑。使用Serial收集器会显着降低应用程序的性能。
03 JVM的垃圾回收机制的更多相关文章
- JVM虚拟机—JVM的垃圾回收机制(转载)
1.前言 理解JVM的垃圾回收机制(简称GC)有什么好处呢?作为一名软件开发者,满足自己的好奇心将是一个很好的理由,不过更重要的是,理解GC工作机制可以帮助你写出更好的Java程序. 在学习GC前,你 ...
- JVM的垃圾回收机制详解和调优
JVM的垃圾回收机制详解和调优 gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存.java语言并不要求jvm有gc,也没有规定gc如何工作.不过常用的jvm都有gc,而且大多数gc都 ...
- JVM的垃圾回收机制 总结(垃圾收集、回收算法、垃圾回收器)
相信和小编一样的程序猿们在日常工作或面试当中经常会遇到JVM的垃圾回收问题,有没有在夜深人静的时候详细捋一捋JVM垃圾回收机制中的知识点呢?没时间捋也没关系,因为小编接下来会给你捋一捋. 一. 技术 ...
- JVM的垃圾回收机制
JVM的垃圾回收机制:(GC通过确定对象是否被活动对象引用来确定是否收集该对象.) 1.触发GC(Garbage Collector)的条件. (1.GC在优先级最低的线程中运行,在未运行的线程中进行 ...
- 秒懂JVM的垃圾回收机制
前言 阅读过王子之前JVM文章的小伙伴们,应该已经对JVM的内存分布情况有了一个清晰的认识了,今天我们就接着来聊聊JVM的垃圾回收机制,让小伙伴们轻松理解JVM是怎么进行垃圾回收的. 复制算法.Ede ...
- 扒一扒JVM的垃圾回收机制,下次面试你准备好了吗
相信和小编一样的程序猿们在日常工作或面试当中经常会遇到JVM的垃圾回收问题,有没有在夜深人静的时候详细捋一捋JVM垃圾回收机制中的知识点呢?没时间捋也没关系,因为小编接下来会给你捋一捋. 一. 技 ...
- JVM java垃圾回收机制
一.jvm简介 1.JVM内存运行时数据区的三个重要的地方 1.1.堆(heap):它是最大的一块区域,用于存放对象实例数组,是全局共享的. 1.2.栈(stack):全称为虚拟机栈,主要存储基本数据 ...
- JVM 及 垃圾回收机制原理
JVM Java 虚拟机 Java 虚拟机(Java virtual machine,JVM)是运行 Java 程序必不可少的机制.JVM实现了Java语言最重要的特征:即平台无关性.原理:编译后的 ...
- JVM:垃圾回收机制和调优手段
转载请注明出处: jiq•钦's technical Blog - 季义钦 引言: 我们都知道JVM内存由几个部分组成:堆.方法区.栈.程序计数器.本地方法栈 JVM垃圾回收仅针对公共内存区域即:堆和 ...
随机推荐
- NtDuplicateObject小解读
源进程和目标进程可以是一个吗 当然执行进程可以是同一个吗 ,当然标志位重要!有一个关闭源进程的标志位 第一步通过ObReferenceHandleTable获得源进程对象(数据结构) //为新的句柄构 ...
- spring boot 文件上传 文件过大 FileUploadBase$SizeLimitExceed
application.properties中加入 multipart.maxFileSizemultipart.maxRequestSize Spring Boot 1.3.x或者之前 multip ...
- django url路由参数错误
出现错误: TypeError get() got an unexpected keyword argument 'teacher_id 出错原因: view类中,get方法获得了一个多余的额参数,这 ...
- linux安全篇
笔者Q:972581034 交流群:605799367.有任何疑问可与笔者或加群交流 1.限制用户su 限制能su到root的用户. 操作步骤 使用命令 vi /etc/pam.d/su修改配置文件, ...
- MySQL--当查询遇到隐藏字符
事件起因: 在将一些EXCEL维护的数据导入MySQL中维护过程中发现漏了一些数据,检查时发现看着相同的SQL返回的结果完全不同: 在SQLyog中看到的截图如: 两个SQL执行返回结果不同,其中一条 ...
- mysql主从延迟高的原因
1.1.1故障1:从库数据与主库冲突 1 2 3 4 5 6 show slave status; 报错:且show slave status\G Slave_I/O_Running:Yes Slav ...
- SQL Server比较2table字段的差异
由于项目前后用了2个数据库,需要统计数据库结构的变化,需要统计每个表的变化,由于人工核对挺浪费时间,就写了一点代码: 1.统计表的字段数量(查询表有多少列): select count(name) ...
- 使用clipboard.js实现复制内容至剪贴板
下载插件 clipboard.js是不依赖flash,实现复制内容至剪贴板的js插件.下载clipboard.js的压缩包,根据需要选择dist目录下的压缩或未压缩版. github地址:https: ...
- template()方法
template(id, data)方法: id:必传,渲染模板的id. data:可选,一个Object对象. return:传data—>渲染完成html代码:不传data—>一个渲染 ...
- ORACLE 建表语句(表名及字段名大写)
ORACLE建表时如果表名或者字段名存在大小写同时存在的情况下,默认为区分大小写,此时在select/updata等操作时需要在表名或者字段名上添加双引号,否则会报"视图不存在"的 ...