前言:作为一个合格Java程序员 ,我觉得对于JVM了解是很有必要。对于代码中常见的内存点以及GC回收策略了解更是很有必要。这是写出高质量程序的基准;
先卖一个关子吧:
观察下列代码 并尝试解释抛内存溢出异常的原因:
public void Test(){
List<User> userInfo = new ArrayList<User>();
while(true){
User user = new User();
user.setUserName("xiaojun");
userInfo.add(user);
}
}
一 JAVA类实际运行时机在JVM中的存贮位置:
(1)PC寄存器(导航)
每个线程都有一个PC寄存器,在线程创建的时候就会进行创建。
他总是指向自己下一条地址的指令。
(2)方法区(通常指的就是静态数据)
类的基本信息(类的基本信息描述)
字段、方法信息
方法的字节码
类的基本信息
(3)堆
线程共享。
通常我们通过new的形式进行创建的对象都会保存在堆之中。
垃圾回收重点区域(新生代 老年代 后续进行介绍)
(4)栈
线程私有保证线程的安全性。
栈之中进行保存栈帧。其中方法的开始和结束对应着入栈和出栈。
栈帧之中保存的是局部变量表。(其中java中的引用就是存贮在栈帧里面的)
GC回收算法解析:
基本思路就是 找到回收对象 回收对象,释放内存。
标记过程有两种:
(1)引用标记算法 进行引用标记需要释放内存 ,完后定期进行GC回收。
(2)可达性分析算法
从根对象开始进行查找引用的链,如果能找到根对象到目标对象的引用的链,那么这个对象就不会被GC,否则就会进行触发GC完成回收的操作。
根对象:
1 局部变量量表之中的对象
2 静态常量应用的对象
3 常量应用的对象
回收过程:
(1)标记复制算法:
基本思路就是在系统内存中进行开辟两块内存结构,完后将没有标记的复制到另一块内存空间之中,完后被标记的内存空间进行整体GC。
特点:高效 但是需要的内存空间大
(2)标记压缩算法:
1 将存活的对象进行压缩到内存的另一边。
2 然后将接下来要进行GC的对象整体放到另一边。整体进行GC
(3)分代回收算法:
新生代:
新生代分为两部分,E区和S区;
新生代里面的对象几乎都是短命对象存活时间不会太久;
采用的是标记-复制算法进行处理;
老年代:
老年代采用的是 标记-压缩算法;
原因是老年代中有大量的存活比较久的对象;
- JAVA对象的生命周期: (1)经过初始化之后进行创建对象。在堆之中进行分配内存。 (2)由于堆是分代的所以一般存储在新生代的E区中。
(3)如果有新生代的对象进行创建的时候,放入新生代的时候发现内存不足的时候会进行一次GC;
(4)通过可达性算法进行分析之后,找出本次要进行GC的对象,然后进行直接GC。
(5)新生代的对象是有生命周期的。没经过一次GC之后年龄进行+1,当超过一定数值之后进行晋升。默认配置的数值是15。
(6)如果首次创建的对象是大对象的操作,则直接进入老年代。
(7)同理在晋升老年代的同时,也会进行内存容量判断,如果内存容量不足则进行GC。所有操作完毕之后发现内存依然不足
就直接进行跑出异常OOM
分配担保问题解析:
简单理解就是 就是当新生代进行GC的时候,会进行判断老年代的持续空间是否大于等于新生代对象容量的总和。如果大于直接进行GC,否则老年代会进行FullGC操作,FullGC会让JVM出现卡顿的现象。
后续待更新。。。。欢迎下方留言补充