1.堆
- 一个JVM实例只存在一个堆空间,堆也是管理java内存的核心区域
- java堆区在jvm启动时就被创建,空间大小就确定,堆是jvm管理的最大一块内存空间,堆得大小是可以设置的
- 堆可以处于物理上不连续的内存空间,但在逻辑上它应该视为连续
- 所有的线程共享堆,在这里还可以划分分线程私有的缓冲区(Thread Local Allocation Buffer )TLAB
- 所有的对象实例以及数组都应该在运行时分配在堆上
- 数组和对象可能永远不会存储在栈中,因为栈帧中保存引用,这个引用指向对象和数组在堆中的位置
- 方法结束后,堆中的对象不会马上被移除,仅仅在垃圾回收时才会被移除
- 堆是垃圾回收器(Garbge Collection)重点回收的对象
2.堆内存逻辑分为
新生代(Young Generation space)+养老区(Old Generation space)+元空间(Meta space)
- 设置堆空间大小 -Xms 表示堆区的起始内存(年轻代+老年代) 等价于 -XX:InitialHeapSize
-Xmx表示堆区的最大内存 等价于 -XX:MaxHeapSize
3.OOM(OutOfMemory)
4.年轻代和老年代
- 存储在jvm的java对象可以划分为两类:一类生命周期特别短,创建和消亡都非常迅速,另一类生命周期很长,极端情况可能与JVM生命周期一直
- java堆区细分为老年代和年轻代 :
1)年轻代又可分为Eden空间、Survivor0 (from区)和Survivor1空间(to区)
2)新生代一般占用1/3的堆空间,其中eden占新生代的8/10,s1区占1/10,s0占1/10,老年代占用2/3堆空间
3)几乎所有的java对象都在eden区就new出来,绝大部分的java对象的销毁在eden区
4)对象分配过程:
a)new的对象先放入eden区,eden有大小限制
b)当eden区装满时,此时需要new对象时,Minor GC会对eden进行垃圾回收,将eden不再使用的对象销毁,并将幸存对象放入空的s0区并计数age为1
c)当eden再次装满时,Minor GC再次垃圾回收,将幸存的对象放入s1区,如果此时s0区的对象再次存活,则方入s1区,计数+1,
d)长此以往,当计数达到15时,就会被放入老年区,计数参数是可以设置的
5.Minor GC、Major GC 和 Full GC
- GC回收,并非每次都是针对三大内存区域(新生代、老年代、方法区)回收
- GC按照回收区域分为部分收集(Partial GC)和整堆收集(Full GC)
- 部分收集:不是完整收集java堆得垃圾收集
a)Minor GC :只是新生代的收集,触发机制:eden区满,s0和s1区满不会触发Minor GC,因为java对象具,备朝生夕灭的特性,所以Minor GC会比较频繁,但处理速度快,当Minor GC回收,会触发STW,暂停用户线程
b)major GC:真是针对老年代收集
c)FullGC:收集整个java堆和方法区的垃圾回收 :触发条件,调用system.gc()
、老年代空间不足、方法区空间不足、通过Minor GC进入老年代平均大小大于老年代的可用内存、在eden区中s0区和s1区复制时对象大于To Space可用
内存则将对象转入老年区,且老年区的可用内存小于该对象大小
6.内存分配策略
针对不同年龄段的对象分配原则
- 优先分配值eden区
- 大对象直接分配到老年代(尽量避免程序出现过多的大对象)
- 长期存活的对象分配至老年代
- 动态年龄分配:如果survivor区中相同年龄的所有对象的大小总和大于survivor空间的一般,年龄大于或者等于该年龄对象可以直接进入老年代,无需等到阈值
- 空间分配担保
7.TLAB
- 为什么要有TLAB?
堆区是线程共享区域,任何线程都可以访问到堆区的共享数据,在并发环境下划分内存空间是线程不安全的,为避免多个线程操作同一地址,需要枷锁,但枷锁会影响分配速度 - 什么是TLAB
从内存模式中,在eden区继续进行划分,为每个线程分配一个私有的缓冲区域,它包含在Eden区,使用TLAB可以避免一系列线程不安全问题,还能提高内存的吞吐,因此我们可以将这种内存分配模式称为快速分配策略
8.逃逸分析 - new对象实体是否有可能在方法外部调用
package com.pingan;
public class EscapeAnalysis {
public EscapeAnalysis obj;
/*
* 方法返回EscapeAnalysis对象,发生逃逸
*/
public EscapeAnalysis getInstance()
{
return obj == null ? new EscapeAnalysis():obj;
}
/*
* 为成员变量赋值,发生逃逸
*/
public void setObj()
{
this.obj = new EscapeAnalysis();
}
/*
* 对象的作用域仅在当期方法有效,没有发生逃逸
*/
public void userEscapeAnalysis() {
EscapeAnalysis s = new EscapeAnalysis();
}
/*
* 引用成员函数,发生逃逸
*/
public void userEscapeAnalysis1() {
EscapeAnalysis s = getInstance();
}
}