JVM系列

Stella981
• 阅读 343

关注Java后端技术栈

回复“面试”获取最新资料JVM系列

本文内容

JVM系列

如何确定一个对象是垃圾对象?

对于一部分人来说先搞清楚,对象与对象引用到底是什么区别?如果还不是很确定这两者的关系,请看:浅谈Java中的对象和对象引用

Java是面向对象编程的语言,都说在Java世界里万事万物皆对象。

类的声明周期和对象的声明周期。关于类的声明周期请看:JVM系列——java文件到JVM中的整个过程

对象的生命周期

下面大致说说对象的生命周期。

JVM系列

创建阶段

在创建阶段有几个步骤完成对象的创建:

  • 为对象分配存储空间

  • 开始构造对象

  • 从超类到子类对static成员进行初始化

  • 超类成员变量按顺序初始化,递归调用超类的构造方法

  • 子类成员变量按顺序初始化,子类构造方法调用

一旦对象被创建,并被分派给某些变量赋值,这个对象的状态就切换到了应用阶段

应用阶段

该对象应该至少被一个强引用持有着。

可以见阶段

当一个对象处于不可见阶段时,说明程序本身不再持有该对象的不论什么强引用,尽管该这些引用仍然是存在着的。简单说就是程序的运行已经超出了该对象的作用域了。

不可达阶段

对象处于不可达阶段是指该对象不再被不论什么强引用所持有。

与“不可见阶段”相比,“不可见阶段”是指程序不再持有该对象的不论什么强引用,这样的情况下,该对象仍可能被JVM等系统下的某些已装载的静态变量或线程或JNI等强引用持有着,这些特殊的强引用被称为”GC root”。存在着这些GC root会导致对象的内存泄露情况,无法被回收。

收集阶段

当垃圾回收器发现该对象已经处于“不可达阶段”而且垃圾回收器已经对该对象的内存空间又一次分配做好准备时,则对象进入了“收集阶段”。假设该对象已经重写了finalize()方法,则会去运行该方法的终端操作。

这里要特别说明一下:不要重载finazlie()方法!finial方法来自Object中。参考:

原因有两点:

  • 会影响JVM的对象分配与回收速度

在分配该对象时,JVM需要在垃圾回收器上注冊该对象,以便在回收时可以运行该重载方法;在该方法的运行时需要消耗CPU时间且在运行完该方法后才会又一次运行回收操作,即至少需要垃圾回收器对该对象运行两次GC。

  • 可能造成该对象的再次“复活”

在finalize()方法中,假设有其他的强引用再次持有该对象,则会导致对象的状态由“收集阶段”又又一次变为“应用阶段”。这个已经破坏了Java对象的生命周期进程,且“复活”的对象不利用兴许的代码管理。

终结阶段

当对象运行完finalize()方法后仍然处于不可达状态时,则该对象进入终结阶段。在该阶段是等待垃圾回收器对该对象空间进行回收。

对象空间又一次分配阶段

垃圾回收器对该对象的所占用的内存空间进行回收或者再分配了,则该对象彻底消失了,称之为“对象空间又一次分配阶段”。

Java对象的四大引用

先来了解对象的四大引用方式:

JVM系列

 JDK从1.2开始增加了多种引用方式:软引用、弱引用、虚引用 。

强引用

    /**
     * /强引用
     */
    private void strangeReference() {
        String str = new String("");
        System.out.println(str);
        UserVo [] userVo = new UserVo[1000000000];
        System.out.println(userVo.toString());
    }

方法中就算内存不够了而导致OOM,也不会进行回收这种引用。也是我们平时对象使用方式最多的方式。

弱引用

/**
 * 软引用
 */
private void softReference() {
    Object obj = new Object();
    SoftReference<Object> sf = new SoftReference<Object>(obj);
    System.out.println(sf.toString());
}

如果一个对象具有软引用,内存足够GC就不会回收它,内存不足时就会回收这些对象的内存,故可以防止内存 泄露,增强代码的健壮性 。软引用用来实现内存敏感的高速缓存,比如说:网页缓存、图片缓存等。

软引用

    /**
     * 弱引用
     */
    private void weakReference() {
        String str = new String("abc");
        WeakReference<String> abcWeakRef = new WeakReference<String>(str);
    }

弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在Java中,用java.lang.ref.WeakReference类来表示。

经典实用案列java.langThreadLocal

JVM系列

ThreadLocal相关请参考:
ThreadLocal造成内存溢出OOM面试再问ThreadLocal,别说你不会
再谈ThreadLocal
ThreadLocal 面试六连问,中高级必问

虚引用

    /**
     * 虚引用
     */
    private void phantomReference() {
        String str = new String("abc");
        ReferenceQueue<String> rq = new ReferenceQueue<String>();
        PhantomReference<String> phantomRef = new PhantomReference<String>(str, rq);
    }

如果一个对象仅持有虚引用,那么它就和没有引用一样,在任何时候都可以被垃圾回收期收回     作用:跟踪对象被垃圾回收器回收的活动。

如何确定一个对象是垃圾对象?

要想进行垃圾回收,得知道什么样的对象是垃圾。

引用计数法

对于某个对象而言,只要应用程序中持有该对象的引用,就说明对象不是垃圾,如果一个对象没有任何引用指向它,那么改对象就是垃圾对象。

在对象头处维护一个counter,每增加一次对该对象的引用计数器自加,如果对该对象的引用失联,则计数器自减。当counter为0时,表明该对象已经被废弃,不处于存活状态。这种方式一方面无法区分软、虛、弱、强引用类别。另一方面,会造成死锁,假设两个对象相互引用始终无法释放counter,永远不能GC

弊端:如果一个对象A持有对象B,而对象B也持有一个对象A,那发生了类似操作系统中死锁的循环持有,这种情况下A与B的counter恒大于1,会使得GC永远无法回收这两个对象 。

可达性分析

通过一系列为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明该对象是不可用的。如果对象在进行可行性分析后发现没有与GC Roots相连的引用链,也不会理解死亡。它会暂时被标记上并且进行一次筛选,筛选的条件是是否与必要执行finalize()方法。如果被判定有必要执行finaliza()方法,就会进入F-Queue队列中,并有一个虚拟机自动建立的、低优先级的线程去执行它。稍后GC将对F-Queue中的对象进行第二次小规模标记。如果这时还是没有新的关联出现,那基本上就真的被回收了。

JVM系列

那么那些点可以作为GC Roots呢?一般来说,如下情况的对象可以作为GC Roots

  1. 虚拟机栈(栈桢中的本地变量表)中的引用的对象

  2. 方法区中的类静态属性引用的对象

  3. 方法区中的常量引用的对象

  4. 本地方法栈中JNI(Native方法)的引用的对象

对于大多数人来说主要是理解上面这四点。要通过这四点去反推,去领悟为什么能把他们当做GC Roots。把四点理解清楚了,理解的这GC Roots就简单了。

往期精彩:

面试被问:缓存击穿有什么解决方案

面试被问:5 亿整数的大文件,排个序 ?

面试被问:高并发下的接口幂等性

面试官问:浏览器输入 URL 回车之后发生了什么?

面试题:数据量很大,分页查询很慢,有什么优化方案?

凭借这套面试题,工资狂涨60%!!!

JVM系列

在这金三银四的季节,栈长为大家准备了几份面试宝典:

  • 《java面试宝典5.0》

  • 《Java(BAT)面试必备》

  • 《350道Java面试题:整理自100+公司》

  • 《资深java面试宝典-视频版》

  • 大量电子书籍

JVM系列

获取方式:点“在看”,V信扫描上面二维码领取。

本文分享 CSDN - 田维常。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这