TLAB:默认给每一个线程开辟一块内存空间存放线程自己的对象
Class对象是存放在堆区的,不是方法区,类的元数据元数据并不是类的Class对象,Class对象是加载的最终产品,类的方法代码,变量名,方法名,访问权限,返回值等等都是在方法区的,代码信息只是在方法区;
对齐填充:方便计算机寻址存取方便,是计算机寻址最优的一种方式
压缩前:一开始String和Object都是占用的是8个字节,这些对象地址放到堆里面,开启某一个参数以后可以让对应的对象的内存地址压缩到四个字节
压缩以后:
init方法:成员变量真正进行赋值,之前是0,并且调用对象的构造方法
标量替换:对于一个对象来说,如果采取栈上分配,不会new一个对象在栈上面,而是将它的成员变量属性剥离开存放分配在栈上,分开存,这几个字段会进行标识是属于哪一个对象的,前提是开启了逃逸分析;
1)大对象直接放到老年代:可以设置参数
如果你知道系统创建的对象比较大,况且这些对象不会被垃圾收集的,Serial+ParNew有效,JVM会直接判断对象大小,就是为了降低大对象分配内存的时候复制对象而降低效率
2)达到分代年龄存放到老年代:可以设置参数
大概知道很多new对象生命周期不是特别长,可能要用一段时间,没有执行时间特别长的方法,或者其他的情况,程序员大概可以估算到一个方法中有一个大对象,但是方法结束非常快,可能一两次GC就被干掉,分代年龄尽量设置的少一些,可以尽快节省新生代的空间
3)对象动态年龄判断机制:
对于订单系统来说,每秒钟有60M对象会向伊甸园区里面存放,1S以后变成垃圾对象
当前放对象的Survior区域里面(其中一块区域,放对象的那一块S区),一批对象的总大小大于这块Survior区域的50%(-XX:TargetSurivorRatio可以指定),那么此时大于等于这一批年龄对象的最大值的对象,就可以直接存放到老年代了,假设现在幸存者区里面有一批对象,年龄1+年龄2+年龄N的多个年龄对象超过了Surivor区域的50%,此时就会把年龄N以上的对象全部放在老年代,这个规则是希望那些可能长期存活的对象尽早地进入到老年代,对象年龄判断机制其实是在minor GC以后触发的;
1)大概13秒或者14s来说伊甸园区就会被放满,会触发minorGC,会把伊甸区的对象全部进行垃圾回收,前面13s的对象做minor GC的时候都是可以回收掉的,但是伊甸区第14s产生的对象会触发minorGC,因为此时订单正在执行过程中,14s产生的对象都被GCROOTS引用着,所以此时这60M对象会被存放到S0区域,但是前面13s产生的对象会被伊甸园区直接被干掉,因为之前伊甸区的方法已经结束了,生成订单非常快;
2)但是最终情况是:这60M对象会被分配到老年代,每隔14s有60M对象放到老年代,等到一段时间5 6min就会发生FullGC,但是其实这些对象其实早就变成垃圾了,因为正常的订单对象早就变成垃圾了1S终究欸有引用指向它,这种情况不太好;
3)这个时候朝生夕死的对象太多,于是就适当提高年轻代的空间大小,频繁导致FullGC的原因就是动态年龄判断机制,使用两种机制来优化,一种是调整surivor区域,一种是把年轻代调整的大一些,几乎不发生FullGC;
适当提升新生代的比例之后:第24s以后对象的空间已经满了,那么此时这个25S产生的60M对象会直接存放到幸存区,此时触发minorGC会进行回收伊甸园区和幸存者区,增大新生代;
4)老年代动态分配担保机制:当JVM在做minor GC之前,如果大概率先做FullGC,再来做minorGC,那么这个minorGC非常快时间非常短,如果你不做这个判断,做完minorGC之后再来做fullGC,那minor GC耗费的时间比较多,FullGC耗费的时间也会很多;
1)JVM会进行判断老年代剩余有效的空间如果小于年轻代所有对象(包含垃圾对象)的空间,如果老年代不能容纳下来新生代的所有对象,假设说如果新生代的所有的对象直接挪到老年代了,放不了就会直接出发fullGC,然后JVM会判断一个参数;
2)如果老年代能容纳下来新生代的所有对象(大于等于),那么直接进行minor GC
3)此时如果你设置了这个参数,那么直接会进行判断老年代可用空间是否小于历史上每一次minor GC之后进入到老年代的对象的平均大小,假设每一次minor GC,第一次挪了50M,第二次挪了60M,第三次挪了100M,那么JVM会计算这三次挪到老年代的平均内存大小,(50+50+100)/3,如果大于,肯定会做FUllGC,再做minorGC;
4)如果小于那么直接做minor Gc;
5)如果没有设置参数,直接做fullGC;
如果调整完新生代的大小之后将晋升到老年代的空间调整成5了,因为这种情况是每25S触发一次minorGC,触发一次GC,分代年龄+1,分代年龄达到5,说明已经过去了好几分钟了,所以说这些已经达到5的对象早就已经变成垃圾了,要么赶紧被清理掉,如果分代年龄达到5,说明这样的对象肯定不是简单的GC对象肯定不容易被收集,这样的对象肯定是系统中的缓存对象,Spring Bean对象,线程池的引用对象,对于这些对象可以让她尽量的早点老年代,不要再年轻代里面占用过多的那些朝生夕死的对象的空间了,订单对象库存对象,优惠劵对象的空间了,让那些年轻代的对象能够在年轻代就被干掉;
使用G1垃圾回收器,内部算法耗费的性能比CMS要高
CMS触发FullGC比例可能会导致部分空间不可用,如果FullGC发生频率很低,这时就可以启动serial Old来进行清理,设置参数是0,每一次FullGC,serial Old会清理一次内存碎片,但是如果出现秒杀活动,就尽量减少内存碎片的整理,因为不敢让用户线程停止,这时候就可以配置5次FullGC一次清理,如果长时间不做内存清理,那么老年代的连续可用内存空间会越来越少;
fullGC回收类元信息和堆空间
没有类原信息,对象的对象头已经没有指针指向这个类原信息,但是这三种类加载器不会被回收,所以加载的类不会被回收;
所有的classLoader都会记录着所有它加载过的类信息
Class对象是可以创建出对象
内存泄漏篇:
常见工具的使用:
1)jps
2)jstat
jstat -class +进程的端口号,类的加载数,占据的总体字节数,卸载数,占用的总体时间
jstat -class +进程的端口号+每隔多少毫秒打印一次,下面的这种情况默认是1000ms打印一次直至程序终止
jstat监视虚拟空间的占用和垃圾回收的行为
jstart -class -t +进程的ID
jstat -gc +进程的PID
jstat -gccause+ID,进行查看gc的原因
能够计算出这两段区间内gc的执行时间,后面GC的间隔时间/程序间隔运行时间,可以看出垃圾回收时间占总共程序运行时间的比例
隔一段时间找一个最小值,隔一段时间找一个最小值
3)