百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

关于JVM和Hotspot,你也许有这么几个容易晕的问题

bigegpt 2024-08-05 11:52 10 浏览

1.JVM的结构到底有哪些?

快速过一遍JVM的内存结构,JVM中的内存分为5个虚拟的区域:

? 你的Java程序中所分配的每一个对象都需要存储在内存里。堆是这些实例化的对象所存储的地方。是的——都怪new操作符,是它把你的Java堆都占满了的!

? 它由所有线程共享

? 当堆耗尽的时候,JVM会抛出java.lang.OutOfMemoryError 异常

? 堆的大小可以通过JVM选项-Xms和-Xmx来进行调整

堆被分为:

? Eden区 —— 新对象或者生命周期很短的对象会存储在这个区域中,这个区的大小可以通过-XX:NewSize和-XX:MaxNewSize参数来调整。新生代GC(垃圾回收器)会清理这一区域。

? Survivor区 —— 那些历经了Eden区的垃圾回收仍能存活下来的依旧存在引用的对象会待在这个区域。这个区的大小可以由JVM参数-XX:SurvivorRatio来进行调节。

? 老年代 —— 那些在历经了Eden区和Survivor区的多次GC后仍然存活下来的对象(当然了,是拜那些挥之不去的引用所赐)会存储在这个区里。这个区会由一个特殊的垃圾回收器来负责。年老代中的对象的回收是由老年代的GC(major GC)来进行的。

方法区

? 也被称为非堆区域(在HotSpot JVM的实现当中)

? 它被分为两个主要的子区域

持久代

这个区域会 存储包括类定义,结构,字段,方法(数据及代码)以及常量在内的类相关数据。它可以通过-XX:PermSize及 -XX:MaxPermSize来进行调节。如果它的空间用完了,会导致java.lang.OutOfMemoryError: PermGen space的异常。

代码缓存

这个缓存区域是用来存储编译后的代码。编译后的代码就是本地代码(硬件相关的),它是由JIT(Just In Time)编译器生成的,这个编译器是Oracle HotSpot JVM所特有的。

JVM栈

? 和Java类中的方法密切相关

? 它会存储局部变量以及方法调用的中间结果及返回值

? Java中的每个线程都有自己专属的栈,这个栈是别的线程无法访问的。

? 可以通过JVM选项-Xss来进行调整

本地栈

? 用于本地方法(非Java代码)

? 按线程分配

PC寄存器

? 特定线程的程序计数器

? 包含JVM正在执行的指令的地址(如果是本地方法的话它的值则未定义)

2.为什么JVM规范里面从来没有出现过永久代这个词?

根据 JVM 规范,JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分:

绝大部分 Java 程序员应该都见过 "java.lang.OutOfMemoryError: PermGen space "这个异常。这里的 “PermGen space”其实指的就是方法区。前者是 JVM 的规范,而后者则是 JVM 规范的一种实现,并且只有 HotSpot 才有 “PermGen space”,而对于其他类型的虚拟机,如 JRockit(Oracle)、J9(IBM) 并没有“PermGen space”。由于方法区主要存储类的相关信息,所以对于动态生成类的情况比较容易出现永久代的内存溢出。最典型的场景就是,在 jsp 页面比较多或者加载的包较多的情况,容易出现永久代内存溢出。因此,你会看到Java8虚拟机规范和Java7并没有什么不同,但是所有人都在告诉你:Hotspot虚拟机舍弃了永久代。

3.JDK7中的永久代会回收吗?它回收哪些东西?

一般的垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(FullGC)。如果仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。那么永久代能回收什么呢?一,废弃的字符串常量,二,不再被引用的class对象。正确的设置永久代大小对避免FullGC是非常重要的原因。所以引起full GC的原因不光是老年代满了或者超过临界值,也有可能是永久代满了或者超过临界值。

4.永久代已经没了。那么JVM规范中的方法区HotSpot是如何实现的?

JDK8的Hotspot使用Metaspace(元空间)代替了永久代。相比较永久代,有如下的一些改变。

其中有如下一段话:

元空间被直接接分配在本地内存。默认的元数据空间分配只受到本地内存的限制。我们可以使用一个新的MaxMetaspaceSize选项来设置元空间占用本地内存的大小。这个选项与MaxPermSize类似。当元空间使用量MetaspaceSize设置的值(32位的client模式默认是12M,32位的server模式是16M,64位的server模式则会更多)达到了垃圾收集器会收集那些不再使用的classloader和class会被回收。所有设置一个较大的MetaspaceSize值会延迟垃圾收集发生的时间。在触发一次垃圾收集以后和下一次垃圾收集之前,元空间的使用值值会随着使用不断增加。

5.Hotpsot垃圾回收的原理是什么?

hotspot的垃圾收集的判定主要是使用可达性分析算法。在目前主流的编程语言(java,C#等)的主流实现中,都是称通过可达性分析(Reachability Analysis)来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。如下图所示,对象object 5、object 6、object 7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象。

6.被判定为垃圾地对象一定会被回收吗?

即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:

如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。(即意味着直接回收)

如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃。

finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了。

代码示例:

public class FinalizeEscapeGC {
 public static FinalizeEscapeGC SAVE_HOOK = null;
 public void isAlive() {
 System.out.println("yes,i am still alive:)");
 }
 @Override
 protected void finalize() throws Throwable {
 super.finalize();
 System.out.println("finalize mehtod executed!");
 FinalizeEscapeGC.SAVE_HOOK = this;
 }
 public static void main(String[] args) throws Throwable {
 SAVE_HOOK = new FinalizeEscapeGC();
 // 对象第一次成功拯救自己
 SAVE_HOOK = null;
 System.gc();
 // 因为finalize方法优先级很低,所以暂停0.5秒以等待它
 Thread.sleep(500);
 if (SAVE_HOOK != null) {
 SAVE_HOOK.isAlive();
 } else {
 System.out.println("no,i am dead:(");
 }
 // 下面这段代码与上面的完全相同,但是这次自救却失败了
 SAVE_HOOK = null;
 System.gc();
 // 因为finalize方法优先级很低,所以暂停0.5秒以等待它
 Thread.sleep(500);
 if (SAVE_HOOK != null) {
 SAVE_HOOK.isAlive();
 } else {
 System.out.println("no,i am dead:(");
 }
 }
}复制代码

运行结果:

finalize mehtod executed!
yes,i am still alive:)
no,i am dead:(复制代码

SAVE_HOOK对象的finalize()方法确实被GC收集器触发过,并且在被收集前成功逃脱了。另外一个值得注意的地方是,代码中有两段完全一样的代码片段,执行结果却是一次逃脱成功,一次失败,这是因为任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法不会被再次执行,因此第二段代码的自救行动失败了。因为finalize()方法已经被虚拟机调用过,虚拟机都视为“没有必要执行”(即意味着直接回收)

写在最后:

码字不易看到最后了,那就点个关注呗,只收藏不点关注的都是在耍流氓

相关推荐

最全的MySQL总结,助你向阿里“开炮”(面试题+笔记+思维图)

前言作为一名编程人员,对MySQL一定不会陌生,尤其是互联网行业,对MySQL的使用是比较多的。对于求职者来说,MySQL又是面试中一定会问到的重点,很多人拥有大厂梦,却因为MySQL败下阵来。实际上...

Redis数据库从入门到精通(redis数据库设计)

目录一、常见的非关系型数据库NOSQL分类二、了解Redis三、Redis的单节点安装教程四、Redis的常用命令1、Help帮助命令2、SET命令3、过期命令4、查找键命令5、操作键命令6、GET命...

netcore 急速接入第三方登录,不看后悔

新年新气象,趁着新年的喜庆,肝了十来天,终于发了第一版,希望大家喜欢。如果有不喜欢看文字的童鞋,可以直接看下面的地址体验一下:https://oauthlogin.net/前言此次带来得这个小项目是...

精选 30 个 C++ 面试题(含解析)(c++面试题和答案汇总)

大家好,我是柠檬哥,专注编程知识分享。欢迎关注@程序员柠檬橙,编程路上不迷路,私信发送以下关键字获取编程资源:发送1024打包下载10个G编程资源学习资料发送001获取阿里大神LeetCode...

Oracle 12c系列(一)|多租户容器数据库

作者杨禹航出品沃趣技术Oracle12.1发布至今已有多年,但国内Oracle12C的用户并不多,随着12.2在去年的发布,选择安装Oracle12c的客户量明显增加,在接下来的几年中,Or...

flutter系列之:UI layout简介(flutter-ui-nice)

简介对于一个前端框架来说,除了各个组件之外,最重要的就是将这些组件进行连接的布局了。布局的英文名叫做layout,就是用来描述如何将组件进行摆放的一个约束。在flutter中,基本上所有的对象都是wi...

Flutter 分页功能表格控件(flutter 列表)

老孟导读:前2天有读者问到是否有带分页功能的表格控件,今天分页功能的表格控件详细解析来来。PaginatedDataTablePaginatedDataTable是一个带分页功能的DataTable,...

Flutter | 使用BottomNavigationBar快速构建底部导航

平时我们在使用app时经常会看到底部导航栏,而在flutter中它的实现也较为简单.需要用到的组件:BottomNavigationBar导航栏的主体BottomNavigationBarI...

Android中的数据库和本地存储在Flutter中是怎样实现的

如何使用SharedPreferences?在Android中,你可以使用SharedPreferencesAPI来存储少量的键值对。在Flutter中,使用Shared_Pref...

Flet,一个Flutter应用的实用Python库!

▼Flet:用Python轻松构建跨平台应用!在纷繁复杂的Python框架中,Flet宛如一缕清风,为开发者带来极致的跨平台应用开发体验。它用最简单的Python代码,帮你实现移动端、桌面端...

flutter系列之:做一个图像滤镜(flutter photo)

简介很多时候,我们需要一些特效功能,比如给图片做个滤镜什么的,如果是h5页面,那么我们可以很容易的通过css滤镜来实现这个功能。那么如果在flutter中,如果要实现这样的滤镜功能应该怎么处理呢?一起...

flutter软件开发笔记20-flutter web开发

flutterweb开发优势比较多,采用统一的语言,就能开发不同类型的软件,在web开发中,特别是后台式软件中,相比传统的html5开发,更高效,有点像c++编程的方式,把web设计出来了。一...

Flutter实战-请求封装(五)之设置抓包Proxy

用了两年的flutter,有了一些心得,不虚头巴脑,只求实战有用,以供学习或使用flutter的小伙伴参考,学习尚浅,如有不正确的地方还望各路大神指正,以免误人子弟,在此拜谢~(原创不易,转发请标注来...

为什么不在 Flutter 中使用全局变量来管理状态

我相信没有人用全局变量来管理Flutter应用程序的状态。毫无疑问,我们的Flutter应用程序需要状态管理包或Flutter的基本小部件(例如InheritedWidget或St...

Flutter 攻略(Dart基本数据类型,变量 整理 2)

代码运行从main方法开始voidmain(){print("hellodart");}变量与常量var声明变量未初始化变量为nullvarc;//未初始化print(c)...