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(); } }