1.堆
GC垃圾回收,主要是在伊甸园区和养老区,没有被回收的存放在幸存区;
假设内存满了,OOM,堆内存不够!会报错
新生区:
类:诞生和成长的地方,甚至死亡;
伊甸园区:所有的对象都是在伊甸园区new出来的;
幸存区(0,1):轻GC回收之后剩下的会进入幸存区。
养老区:
新生区内存占完了,存入养老区
永久区:
元空间:逻辑上存在,物理上不存在;
这个区域常驻内存,用来存放JDK自身携带的Class对象,interface元数据,存储的是java运行时的一些环境或类信息,这个区域不存在垃圾回收。(关闭VM虚拟机就会释放这个区域的内存)
jdk1.6之前:永久代,常量池在方法区中;
jdk1.7:永久代,慢慢退化了,常量池在堆中;
jdk1.8之后:无永久代,常量池在元空间中。
2.OOM
一个启动类,加载了大量的第三方jar包,Tomcat部署了太多的应用,大量动态生成的放射类,不断被加载,直到内存满,就会出现OOM。
1.尝试扩大堆内存,看结果(VM:-Xms1024m -Xmx1024m -XX:+PrintGCDetails)
2.分析内存,看一下哪个地方出了问题(内存快照工具Jprofiler:能够看到代码第几行出错)
Jprofiler作用:
- 分析Dump内存文件,快速定位内存泄露;
- 获得堆中的数据
- 获得大的对象
//-Xms 设置初始化内存分配大小,默认1/64
//-Xmx 设置最大分配内存,默认1/4
//-XX:+PrintGCDetails 打印垃圾回收信息
//-XX:HeapDumpOnOutOfMemoryError //oom Dump检查内存问题
3.GC(垃圾回收)
JVM再进行GC时,并不是对这三个区域统一回收,大部分时候回收的是新生代~
- 新生代
- 幸存区(from,to)会交换,谁空谁是to
- 老年区:当一个对象经历了15次GC,还没有死,就会进入老年区
GC两种类:轻GC(普通的GC),重GC(全局的GC)
GC的算法:
(1)引用计数法:引用计数法通过在对象头分配一个字段,用来存储该对象引用计数。一旦该对象被其他对象引用,计数加 1。如果这个引用失效,计数减 1。当引用计数值为 0 时,代表这个对象已不再被引用,可以被回收。
(2)复制算法:作用在新生代,每次GC都会将Eden中活的对象移到幸存区中:一旦Eden区被GC后,就会是空的!
-
优点:没有内存碎片
-
缺点:浪费了内存空间,多了一半空间永远是空to
最佳适用场景:对象存活度较低的情况!
(3)标记清除法:
-
优点:不需要额外的空间
-
缺点:两次扫描,严重浪费时间,会产生内存碎片
(4)标记压缩:在标记清除的基础上增加一个压缩的过程,再次扫描,多一个移动成本,将存活的对象排个序。
总结:
内存效率:复制算法>标记清除算法>标记压缩算法(时间复杂度)
内存整齐度:复制算法=标记压缩算法>标记清除算法
内存利用率:标记清除算法=标记压缩算法>复制算法
注:没有最好的算法,只有最合适的算法!