JVM学习笔记(一):垃圾回收机制?原理?内存泄漏?内存溢出?
面试中经常会被面试官问到关于JVM的知识,由于一直在大学的象牙塔中,也没有好好了解过其中的奥秘。
觉得这一块也挺重要的,所以专门找了一些资料,学习了一下。
1.什么是垃圾回收机制
垃圾回收机制,也叫做GC。这可以说是java最强的特性之一,GC直击C++开发痛点,无需开发人员手动管理内存,只需要放肆的New即可。Java会自动回收过期的对象。
但是很不幸,Java也存在内存泄漏。
2.GC是如何判断可以回收的?
GC有两种方式判断是否回收对象。一种是引用计数法,另一种是可达性分析。
1.引用计数法
引用计数法,就是一个对象,被引用一次,就+1,当引用数为0的时候,就说明该对象不再被引用,不再被需要,此时就会被GC回收,但是。。。。
当A引用了B,B又引用了A,两个对象互相引用,那么这么两个对象永远不会被GC所回收。
2.可达性分析
可达性分析。从GC ROOTS开始向下搜索,搜索经过的路成为引用链,当一个对象到GC ROOTS无任何引用链可达时,证明该对象是不可用的。则判断对象可以进行回收 。
引用链可以解决循环引用的的问题。
GC ROOTS包括以下这么对象:
- 虚拟机栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI[即一般说的Native]引用的对象
3.什么是内存泄漏
内存泄漏就是在程序运行中,已经用不到的对象,但是因为某种原因,躲过了上面两种回收检测机制,GC没有进行回收,从而该对象一直占用在内存中。
偶尔的内存泄漏对程序不会有太大的影响,但是积少成多,不管多大的内存,迟早会被这些无用的对象耗尽内存,最终造成内存溢出
可以说这是程序编码中的漏洞,程序的减分项。
4.什么时候会出现内存泄漏
1.当一个长生命周期的对象引用了一个短生命周期的对象时,就会发生内存泄漏。比如下面的这段代码:
1 public class LongLifeCycle(){ 2 public Object object; 3 public void AMethod(){ 4 object = new Object; 5 } 6 }
比如这种情况,如果调用了Amethod()方法,就new了一个object的引用,但是我们也许仅仅只在这个方法中需要用到这个object对象;当这么方法结束时,我们就希望GC会将这个object回收。
但是,类中全局的object对象一直持有着引用,所以GC并不会如我们希望的这样做。这么一个小小的object也许在小体量的程序中掀不起什么大浪,但是如果我们有百万级的用户,并且这个类是多例的。
那么这就是一个大问题了。
解决方法也很简单。就是将现在的全局变量写到方法中,变成局部变量就好了。这也算是一点点细节。。
2.集合中的内存泄漏
看了之后才发现,我一直以来都有着这样的错误。
很多时候,在循环添加数据时,为了保证对象引用的不同,有些值我们不能定义在循环的外面。所以一旦集合对象(比如 HashMap、ArrayList 等)发生了泄漏,会连带着很多对象一起泄漏。
ArrayList<Object> list = new ArrayList<>(); for(int i=0;i<10;i++){ Object object = new Object(); list.add(); object =null; }
这种时候我们创建了十个Object对象,每次循环中虽然释放了object对象。但是list中依然引用着object对象(虽然不是同一个object对象)所以GC不会进行回收,只有objext从list中移除,或list=null时;才会将这一串数据全部释放掉。所以在创建一些数据体量比较庞大的集合时,一定要多个心眼。看看是否会及时释放。
4.内存泄漏和内存溢出
内存泄漏和内存溢出,两者的概念是完全不一样的,但是他们有着一些关联。
内存溢出(out of memory):当程序在申请内存时,没有足够的内存供其使用,就会出现out of memory
内存泄漏(memory leak):当一个对象不在被使用,但是一直占据着一个内存时,就发生了一次内存泄漏,就像上面说的,偶尔一次泄漏可能不是什么问题。
但是大量的泄漏就是程序弊病。内存泄露堆积后,无论多少内存,迟早会被占用光,内存泄漏是内存溢出的诱因之一