学习目标:
内存泄漏与内存溢出 浅析学习目标:
- JVM内存泄漏与内存溢出的区别?
- 内存抖动是什么?怎么解决?
- 垃圾回收机制是什么样的?
学习内容:
1.JVM内存泄漏与内存溢出的区别?
- 内存泄漏
程序在申请内存后,被某个对象一直持有,无法释放已经申请的内存空间,一次而内存泄漏危害不大,但是堆积起来就很严重了,无论多少内存,随着时间的推移,迟早都会用光。 - 内存溢出
程序在申请内存时,没有足够的内存空间供其使用,出现out of memory,Android系统为每个应用程序申请到的内存有限,一般为64M或者128M,我们也可以在AndroidMainfest中配置android:largeheap="true"
,从而给App申请更大的内存空间。
图 1-1
这是一张经典的JVM虚拟机原理图,内存溢出一般发生在堆和虚拟栈中,而内存泄漏则发生在方法区,虚拟栈以及堆中。
2.内存抖动是什么?怎么解决?
内存抖动是指在短时间内有大量的对象被创建或被回收。怎么解决? 复用
3.垃圾回收机制是什么样的?
首先这里就可以先看一下上面的图1-1
然后通过代码的方式来讲解。
public class Fruit{
static int x = 10;
static BigWaterMelon bigWaterMelon_1 = new BigWaterMelon(10);
int y = 20;
BigWaterMelon bigWaterMelon_2 = new BigWaterMelon(y);
public static void main(String[] args){
Fruit fruit = new Fruit();
int z = 30;
final BigWaterMelon bigWaterMelon_3 = new BigWaterMelon(z);
new Thread(new Runnable(){
@Override
public void run(){
int k = 100;
setWeight(k);
}
}).start();
void setWeight(int waterMelonHeight){
fruit.bigWaterMelon_2.weight = waterMelonHeight;
bigWaterMelon_3.weight = waterMelonHeight;
}
}
}
class BigWaterMelon{
private int weight;
public BigWaterMelon(int weight){
this.weight = weight;
}
}
代码敲完,这里我就画了一张简易的图片,大致描述了一下上面的代码在虚拟机里的执行过程。
java垃圾回收算法中之前是引用计数器
,意思就是给每个对象分配一个计算器,当有引用指向这个对象时,计数器加1,当指向该对象的引用失效时,计数器减1。最后如果该对象的计算器为0时,java垃圾回收器会认为该对象是可回收的。
- 优点:
实时性
无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收。
应用无需挂起 在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不足,则立刻报outofmember 错误。
区域性
更新对象的计数器时,只是影响到该对象,不会扫描全部对象
- 缺点:
每次对象被引用时,都需要去更新计数器,有一点时间开销。另外无法解决循环引用问题。
所以就诞生了标记 - 清除
该算法有两个阶段。
- 标记阶段:找到所有可访问的对象,做个标记
- 清除阶段:遍历堆,把未被标记的对象回收
- 缺点:
清理完过后会造成内存碎片过多,当存入大的对象时,无法找到足够的连续内存,而不得已会再次触发GC。
然后就诞生了标记 - 整理
首先需要标记出存活的对象,然后整理就是把存活的对象往一端推。然后就清除边界以外的区域即可。
这个算法就避免了碎片的问题。一般年轻代中执行GC后,会有少量的对象存活,就会用复制算法,只要付出少量的存活对象复制成本就可以完成收集,而老年代中因为对象存活率高,没有额外过多的内存空间分配,就需要标记-整理算法来进行回收。
而Android 的垃圾回收机制,就不一样了,因为Android手机的内存很宝贵,所以会有所变化。
每次创建对象都是在生存区,当伊甸园区(生成区)的空间用完时候,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC,也叫轻GC 或者是YGC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后伊甸园中的剩余对象移动到幸存0区(也叫FromSpace)。若幸存0区也满了,再对该地区进行垃圾回收,然后移动到1区(也叫ToSpace)。
如果1区也满了怎么办呢?在经历了15次YGC后,幸存1区也满了,那么这个时候,JVM将会把这些数据移动到养老区。如果养老区也满了,这个时候就会进行MajorGC(也称Full GC 检查FGC)。执行full GC对养老区的内存进行清理。如果养老区执行了Full GC之后,发现依然无法进行对象的保存,这个时候就会出现OOM(OutOfMemoryError)异常了。
那我们在什么条件下就可以判定对象可以回收了?
我们的new 出来的对象都会引用一个GC Root,通过这个来判定是否回收。
GC Root对象
通过可达性算法,成功解决了引用计数所无法解决的问题-“循环依赖”,只要你无法与 GC Root
建立直接或间接的连接,系统就会判定你为可回收对象。那这样就引申出了另一个问题,哪些属于 GC Root。 在 Java 语言中,可作为 GC
Root 的对象包括以下4种:虚拟机栈(栈帧中的局部变量表)中引用的对象 方法区中类静态属性引用的对象 方法区中常量引用的对象 本地方法栈中 JNI(即一般说的
Native 方法)引用的对象