对象与垃圾回收机制

5 篇文章 0 订阅

一、虚拟机中对象的创建过程

在这里插入图片描述
类加载: 把class加载到内存中去

检查加载: 有没有加载进来 等等检查

遇到new 第一步是检查 能不能在 常量池方法区的常量池定位到类的符号引用
检查加载成功后 分配内存

分配内存有两种方式,两种方式划分内存:指针碰撞、空闲链表

指针指向最后一个空间,只能在堆空间很规整的办事(指针碰撞) ,垃圾回收后就不规整了零散的不规整的怎么办
空闲的和占用的交替的话用不了指针碰撞 所以用空闲链表

堆空间的规整程度决定的用两种方式中的哪个

垃圾回收器带有整理(压缩)功能
带整理功能的就会使得内存规整,没有带整理的就不规整
(CMS)不带整理的垃圾回收器,只能使用空闲链表
带有整理的话使用指针,指针快一些,不需要查表

不管用什么分配内存方法同样可以用到多线程
多线程就会遇到安全问题

两种方式解决安全问题:CAS 加失败重试 、本地线程分配缓冲

本地线程分配缓冲:(Thread Local Allocation Buffer TLAB)
堆中分配成 Eden From to tenured
每个线程划分一块区域
在Eden区划分 ,不会再其他区划分

效率比较高
让我们对象分配更快,减少通过比较交换或者同步方法带来的性能的开销
在这里插入图片描述
TLAB只占 Eden 1% 占满了 再重新画一块
默认开启TLAB
新生代不够就拓展,实在没得拓展就OutofMemory
内存控件初始化 设置零值
设置:对象属于哪个实例

内存空间处理化:
分配完后是空的,需要把一些数据设置为零值
int = 0
boolean = false
不需要赋值就可以马上使用

设置对象是哪个实例的,设置对象头

对象初始化:
之后是构造方法

二、对象的内存布局

在这里插入图片描述
Hotspot虚拟机里面对象必须是8字节的整数,如果对象头和实例数据加起来是8的倍数,就不用填充了

三、对象的访问定位

对象的访问针对所有的虚拟机

两种方式: 使用句柄 直接指针

使用句柄好处:对象移动了,句柄池不需要改
坏处是:二次查找,指针定位的开销

在这里插入图片描述

直接指针坏处:对象移来移去的话reference需要改变
但是可以节约效率
hotspot等主流的虚拟机用的是直接指针的定位方式

两种方式的唯一区别就是 实例是否为直接指向,还是二次映射

堆空间满了,JVM进行垃圾回收(垃圾收集)
收集时要确定哪些对象活的 ,哪些对象死的

四、判断对象的死活两种方法:引用计数法、可达性分析

引用计数法

Python
一个地方引用了他 计数器就+1 失效了就-1 ===0 没人用 就回收
存在一个问题:对象相互引用
相互引用 外部没有与其连接这也是死
Python虚拟机 需要额外的机制(补偿机制)

可达性分析(根可达)

hotspot
根据一条链路追踪的

在这里插入图片描述

/**
 * VM Args:-XX:+PrintGC
 * 判断对象存活
 */
public class Isalive {
    public Object instance =null;
    //占据内存,便于判断分析GC
    private byte[] bigSize = new byte[10*1024*1024];

    public static void main(String[] args) {
        Isalive objectA = new Isalive();
        Isalive objectB = new Isalive();
        //相互引用
        objectA.instance = objectB;
        objectB.instance = objectA;
        //切断可达
        objectA =null;
        objectB =null;
        //强制垃圾回收
        System.gc();
    }
}
[GC (Allocation Failure)  11900K->10787K(15872K), 0.0033926 secs]
[Full GC (Allocation Failure)  10787K->10786K(15872K), 0.0040005 secs]
[Full GC (System.gc())  21171K->567K(36352K), 0.0043743 secs]

Object类里面有一个finalize方法
如果你写的类覆盖了这个方法

    protected void finalize() throws Throwable { }
/**
 * 对象的自我拯救
 */
public class FinalizeGC {
    public static FinalizeGC instance = null;
    public void isAlive(){
        System.out.println("I am still alive!");
    }
    @Override
    protected void finalize() throws Throwable{
        super.finalize();
        System.out.println("finalize method executed");
        FinalizeGC.instance = this;
    }
    public static void main(String[] args) throws Throwable {
        instance = new FinalizeGC();
        //对象进行第1次GC
        instance =null;
        System.gc();
        Thread.sleep(1000);//Finalizer方法优先级很低,需要等待,单独线程跑
        if(instance !=null){
            instance.isAlive();
        }else{
            System.out.println("I am dead!");
        }
        //对象进行第2次GC
        instance =null;
        System.gc();
        Thread.sleep(1000);
        if(instance !=null){
            instance.isAlive();
        }else{
            System.out.println("I am dead!");
        }
    }
}
finalize method executed
I am still alive!
I am dead!

finalize只能执行一次,sleep去掉的话救不活因为线程优先级低,所以这方法并不可靠,

I am dead!
finalize method executed
I am dead!

五、各种引用

强引用 =
(比如女儿) 只要方法在执行,我们的局部变量表就是GCRoots, 垃圾回收器回收不掉

软引用 SoftReference
(比如老婆) 即将OutofMemory ,也会垃圾回收掉

弱引用 WeakReference
(比如女朋友) gc 只要垃圾回收器回收,弱引用就会被回收,空间不够才会GC

虚引用 PhantomReference
(比如刚认识) 定义出来随时被回收掉

六、对象的分配策略

对象的分配原则:

  1. 对象优先在Eden分配
  2. 空间分配担保
  3. 大对象直接进入老年代
  4. 长期存活的对象进入老年代
  5. 动态对象年龄判定

优化技术:

栈中分配对象 逃逸分析
堆中的优化技术 本地线程分配缓冲(TLAB)

在这里插入图片描述
new 之后是第一个优化技术 是否栈上分配
几乎所有的对象都在堆中分配,但不是100%,也可以在栈上分配
栈上分配(虚拟机栈)需要一个技术:逃逸分析
栈上分配是不需要垃圾回收的,不用考虑垃圾回收

对象满足逃逸分析,不会逃出方法体,同时不会逃出线程,栈上分配,不用回收

如果不满足逃逸分析
那么 本地线程分配缓冲(TLAB)
如果不符合TLAB,会判断 是不是大对象
Eden 伊犁园空间

如果是大对象(数组或者字符串),会直接放到老年代,就不用晋级了,老年代的大小大一些
新生代(Eden 、From、To )只占堆空间1/3
老年代占堆空间2/3
所以大对象放到老年代会避免被回收,因为老年代空间大一些

-Xms 30M 
-Xmx 30M

Tenured 20m
Eden 8M
From 1M
To 1M

8:1:1

Eden 区只存放新生对象
里面的对象要么被回收掉,如果存活了,那就不会在Eden区存留 ,
会进入From区,对象头age+1
如果又有一次垃圾回收,存活了下来,age再次+1,进入To区
如果又有一次垃圾回收,存活了下来,age再次+1,进入From区

在From和To之间晃来晃去,这是 复制回收算法

复制回收效率高:只需要把存活的复制过去,处理剩下的就可以了,适合新生代,大部分对象new出来后会很早失效

From区 和 To区 空间往往是一样的,内存利用率只有一半,空间利用率50%

为什么还要有Eden区
经过调查大部分对象是朝生夕死的
大数据分析90%以上的对象会第一次被回收掉,存活的只有10%,那我们放入From区,这样的话浪费的时间就是10%,空间利用率为90%
Eden区的对象是没有年龄的
From区和To区有年龄,经过一次GC存活年龄就加一。
年龄比特位用4位,年龄能记录的最大值1111=15,年龄最大值15
达到15次后进入老年代

进入老年代后就没有age了

垃圾回收器回收新生代叫 minorGC
垃圾回收器回收老年代叫 majorGC/fullGC

如果老年代要满了怎么办
悲观策略:每次进入老年代都要进行一次majorGC/fullGC
很消耗效率
所以提出了 空间分配担保
jvm来担保
只有实在是进不去了才会majorGC/fullGC

动态年龄判断 Survivor空间(from to) ,Eden进survivor进不去了 直接进 老年区

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值