注意
- 在 hotspotVM 中把虚拟机栈和本地方法栈合为了一个栈区
线程共享区
方法区
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
-
ClassLoader 加载类信息
-
常量、静态变量
-
编译后的代码
-
会出现 OOM
-
运行时常量池
-
public static final
-
符号引用类、接口全名、方法名
java 堆 (本次需要优化的地方)
-
虚拟机能管理的最大的一块内存 GC 主战场
-
会出现 OOM
-
对象实例
-
数据的内容
随着程序的运行,内存中的实例对象、变量等占据的内存越来越多,如果不及时进行回收,会降低程序运行效率,甚至引发系统异常。
目前虚拟机基本都是采用可达性分析算法,为什么不采用引用计数算法呢?下面就说说引用计数法是如果统计所有对象的引用计数的,再对比可达性分析算法是如何解决引用计数算法的不足。下面就来看下这 2 个算法:
引用计数算法
每个对象有一个引用计数器,当对象被引用一次则计数器加一,当对象引用一次失效一次则计数器减一,对于计数器为 0 的时候就意味着是垃圾了,可以被 GC 回收。
下面通过一段代码来实际看下
public class GCTest {
private Object instace = null;
public static void onGCtest() {
//step 1
GCTest gcTest1 = new GCTest();
//step 2
GCTest gcTest2 = new GCTest();
//step 3
gcTest1.instace = gcTest2;
//step 4
gcTest2.instace = gcTest1;
//step 5
gcTest1 = null;
//step 6
gcTest2 = null;
}
public static void main(String[] arg) {
onGCtest();
}
}
分析代码
//step 1 gcTest1 引用 + 1 = 1
//step 2 gcTest2 引用 + 1 = 1
//step 3 gcTest1 引用 + 1 = 2
//step 4 gcTest2 引用 + 1 = 2
//step 5 gcTest1 引用 - 1 = 1
//step 6 gcTest2 引用 - 1 = 1
很明显现在 2 个对象都不能用了都为 null 了,但是 GC 确不能回收它们,因为它们本身的引用计数不为 0 。不能满足被回收的条件,尽管调用 System.gc() 也还是不能得到回收, 这就造成了 内存泄漏 。当然,现在虚拟机基本上都不采用此方式。
可达性分析算法
从 GC Roots 作为起点开始搜索,那么整个连通图中额对象边都是活对象,对于 GC Roots 无法到达的对象便成了垃圾回收的对象,随时可能被 GC 回收。
可以作为 GC Roots 的对象
-
虚拟机栈正在运行使用的引用
-
静态属性 常量
-
JNI 引用的对象
GC 是需要 2 次扫描才回收对象,所以我们可以使用 finalize 去救活丢失的引用
@Override
protected void finalize() throws Throwable {
super.finalize();
instace = this;
}
到了这里,相信大家已经能够弄明白这 2 个算法的区别了吧?反正对于对象之间循环引用的情况,引用计数算法无法回收这 2 个对象,而可达性是从 GC Roots 开始搜索,所以能够正确的回收。
不同引用类型的回收状态
强引用
Object strongReference = new Object()
如果一个对象具有强引用,那垃圾回收器绝不会回收它,当内存空间不足, Java 虚拟机宁愿抛出 OOM 错误,使程序异常 Crash ,也不会靠随意回收具有强引用的对象来解决内存不足的问题.如果强引用对象不再使用时,需要弱化从而使 GC 能够回收,需要