jvm内存自动管理机制

对象的创建

java堆在分配内存的时候,主要有2种方式:

1.指针碰撞

将已经使用的内存放一块,未使用的放一块.新增对象的时候,指针移动一个对象大小的距离.

2.空闲列表

新增一个列表,记录着堆中的内存块,哪些是可用的,哪些是不可用的.

区别:

指针碰撞实现相对简单,执行效率高.但是需要内存时规整的.Serial和ParNew收集器带有内存压缩整理功能.因此使用指针碰撞效果很好.

并发问题

综合上面2种方法,在并发的执行对象内存分配的时候,容易发生A对象分配内存时候,指针还没来得及修改,B对象就使用了原来的指针分配了内存.

解决:

  1. 对分配内存的动作进行同步处理,即使用cas+重试.(默认策略)

  2. 每个线程在堆中预先分配一个本地线程分配缓冲.(tlab).先在tlab中分配内存,用完了在同步方式分配.

对象的访问

1.直接指针访问,hotspot所使用的方式.

2.通过句柄访问

区别

直接指针访问,节省一次指针定位的开销,速度上稍微快些.

句柄访问时,局部变量表中存储的是句柄池的地址,对象移动时,只需要改变后面的指针,不需要改变对象的引用的地址.

垃圾回收机制

引用类型

  1. 强应用.

    默认的引用.只要引用关系存在,就不会进行gc.

  2. 软引用.

    当gc后,如果jvm内存不足,则会回收掉软引用.

  3. 弱引用.

    不管jvm内存会不会不足,在gc的时候,都会收回弱引用.

  4. 虚引用.

    主要是用来,对象在回收后,得知这个通知.

寻找GC对象

  1. 引用计数法

    为对象添加一个计数器,每有一个地方引用,就+1.引用失效就-1.

    问题:如果2个对象互相引用,则始终无法回收这2个对象.

  2. 可达性分析算法

    由gcroots开始,根据引用关系搜索出一个引用链.这个引用链之外的对象,就是需要回收的对象.

    可做为GcRoot的对象主要有如下:

    a. 虚拟机栈中的栈桢的本地方法引用的对象.

    b.方法区中的类静态变量.

    c.方法区的常量.

    d.jni使用的对象.

垃圾回收算法

标记-清除法

标记出需要回收的对象,然后再统一回收.

缺陷:

  1. 在大部分对象都需要回收的时候,需要进行大量的标记和清除动作.

  2. 清除后,造成空间碎片化.

标记-复制法

将新生代按照8:1:1的比例,划分为Eden和2个survivor区.每次只使用其中一个Eden和survivor区.需要回收的时候,将其中存活的对象复制到空闲的survivor,然后清空eden和另外一个survivor.

由于是从绝大多数需要gc的对象中挑出存活的.因此适用于新生代这一出生率高死亡率低的区域.

标记-整理法

在存活率高的老年代中,复制太多的对象,必然效率下降.在这样的区域,将存活的对象移动到一边.然后统一清除其他区域.

对象内存分配策略

1.对象优先在Eden区分配.

2.大对象直接进入老年代.

指定了-XX:PretenureSizeThreshold<字节大小>,默认值是0,大于该设置值的对象直接在老年代分配.

3.长期存活的对象直接进入老年代.

在survivor区,每进行一次minor gc,存活的对象年龄都会+1.在默认15岁后,会进入老年代.

4.动态年龄判断.

如果survivor区相同年龄的对象大于survivor一半时,会将大于或者此年龄的对象挪进老年代.

5.空间分配担保.

加入新生代survivor区无法复制进去minor gc后存活下来的对象的时候,会将放不下的对象挪进老年代.

Last updated

Was this helpful?