本文是阅读《深入理解Java虚拟机》后的总结
一个很形象的比如 Java虚拟机有一堵围墙 外面的人想进来 里面的人要出去 对应了Java虚拟机的自动内存分配和垃圾回收机制
1.首先Java虚拟机的内存分区
堆:最大的一块 所有的对象实例以及数组都要在堆上分配内存 堆中会分新生代和老年代
栈:分虚拟机栈和本地方法栈 对应的是Java的方法 每个方法执行的时候都是会分配一个栈存放着各种变量值 他们之间的差别 虚拟机栈是执行虚拟机的Java方法的栈 本地方法执行的是native方法 都outofmenory
方法区:都是线程私有 主要是一些常量信息 比如一些类信息 静态变量
里面有运行时常量池
程序计算器 主要负责记录程序运行到了哪里
2.什么时候会发生OutOfMemoryError
堆溢出:如果堆只有20M 然后我一直循环
while(true){
list.add(new Person);
}
栈溢出(虚拟机栈和本地方法栈溢出):
StackOverflowError :如果线程申请的栈深度大于虚拟机允许的栈的最大深度 递归的时候发生比较多
OutofMemoryError: 如果在扩展栈时无法申请足够的内存空间
方法区和运行时常量池溢出:
3.下面正式进入主题了 那么Java虚拟机到底是怎么内存动态分配和垃圾回收的呢?
1)如何判断对象该回收:
引用计数法就不说了 因为它无法解决循环引用的问题 基本实践不会用到
可达性算法:就是有一些GC roots (比如虚拟机栈中的对象 方法区常量引用的对象 方法区中类静态属性引用的对象 反正就是一些对象)
如果那些对象和GC roots没有任何关联 那么就可以认为可以回收
2)卧槽 那引用到底是什么意思?
如果reference类型的数据中存储的数值代表的是另一块内存的起始地址 那么久称为一个引用
又分为强引用 软引用 弱引用 虚引用
4.好了 垃圾确定了 那么到底具体怎么回收垃圾来了
1)标记清除算法:这个比较简单 效率也不高 碎片化严重 顾名思义 就是确定了垃圾 就直接删除这些垃圾
2)复制算法:就是将内存分为2块 只是使用其中的一块 需要清除的时候 就将那块存活的对象复制到还没使用的那一块 然后整块清除
3)标记-整理算法 复制算法不太适合老年代 所以提出了标记整理算法不是直接对对象进行回收 而是将所有存活的对象向一端移动然后直接清除掉边界以外的内存
4)现代虚拟机都是采用分代收集算法了 就是按照不同情况采用不同的清除算法呗
5)内存分配
这个更简单 其实就是在堆中分配内存 我们知道 堆又分新生代和老年代
多数的对象在新生代Eden区分配 大对象直接进入老年代 长期存活的对象也将进入老年代