java的内存和默认空间大小
堆-Xms:默认四分之一的内存(4G物理内存就是1G)。其中新生代和老年代1:2。新生代中,eden和survior from、survior to 是8:1:1。
栈-Xss:默认512k-1024k。
方法区-XX:MetaspaceSize:默认21M(64位windows),最大值-1(不设上限)。jvm会自动调整。
类加载过程
1、判断是否加载类。
2、加载类,划内存空间。
如何划分空间?
一个是指针碰撞,连续的空间,默认使用这种
一个是空闲列表,非连续的空间。
如何解决并发同时创建空间的问题?
一个是CAS,一个是TLAB本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)
划分空间涉及到指针压缩、对象的内存分配流程等
3、初始化。符号引用转为直接引用,基础字段设置为零值
4、设置对象头。对象头、示例数据、对其填充。
5、执行init方法。程序员写的内容,程序员赋的值,与上面的零值不同,执行构造方法
指针压缩
64位中对象的地址值可以从64位,压缩到32位。2的32次方是4G,所以4G以下的内存,可以进行指针压缩,jvm会动态调整,默认开启。
对象的内存分配流程
1、在栈中分配——逃逸分析+标量替换。
对象进堆,但有特殊情况,是在栈中分配
如果方法中变量没有被外面的方法使用到(这是逃逸分析),而且这个变量(对象,可分可拆解)可以替换为标量(不可分不可拆解、java中就是基础类型int、boolean之类),即把对象(聚合量)中的成员变量(标量)拿出来,存到栈中,因为栈中可以存这些变量,但一般不能存对象(这就是标量替换)。这样对象就分配到栈中,而不是堆中,随着方法的结束、栈的结束而释放。
2、大对象直接进老年区。
3、不是大对象进eden区
不是大对象则进eden区,eden区满了,就minor gc,清空eden和survior from区,把剩下的数据放入survior to区,如果后面来的对象又把eden撑满了,则minor gc,清空eden和survior to 区,把剩下的数据放入survior from 区,form和to循环往复,如果from/to放不下,就放入老年区。如果老年区满了,就进行full gc。full gc是minor gc的10倍消耗。
4、长期存活对象进老年代
进入一次survior年龄就加一,15岁就进入老年代
5、对象动态年龄判断
如果好几个对象占用survivor50%以上,那这几个对象对应的年龄的数据进入老年代。这是因为15岁不一定准,那如何判断几岁呢?就用这个动态年龄,如果有多个数据大于50%,则说明这多个数据的年龄应该是一个门槛了,可以进入老年代了。
回收
堆满了回收的方法
引用计数法——不能解决循环引用的问题
可达性算法——gc root。根节点是本地方法栈的变量(系统的)、栈中本地变量(当前线程正在使用的)、静态变量(全局在使用的)
常见的引用类型
强引用new User(),gc不回收
软引用new SoftReference<User>(new User()),full gc后还是没空间,就会回收软引用,常用
弱引用new WeakReference<User>(new User()),gc直接回收,不常用
虚引用,不用
finalize()方法
如果是不可达对象,在回收前会走finalize方法。如果finalize方法重写了,且方法里面重新把对象引用上了,就不回收了,否则还是要回收
方法区回收的是无用的类,类没有实例、类的加载器被回收、类class对象没被使用