深入解析java虚拟机:垃圾回收,垃圾回收基础概述
yuyutoo 2024-10-11 23:55 7 浏览 0 评论
垃圾回收基础概述
垃圾回收机制最早诞生于Lisp编程语言,但Lisp的作者McCathy在第一次现场演示Lisp时却因中途耗尽全部32KB内存以及一些其他原因只能草草收场。60年后的今天,垃圾回收技术再也不是一个笑话,它俨然成为诸如Java、C#、Python、Erlang、Golang编程语言的核心组件。
Java最吸引人的特性之一就是它的垃圾回收技术:程序员负责创建对象、使用对象,垃圾回收器负责回收资源,做好善后工作。它从GCRoot出发标记存活对象,清理未被标记的对象,这种方式又被称为追踪式回收。Java的所有垃圾回收器都使用追踪式回收,只是具体的算法细节不尽相同。本章将讨论HotSpot VM中现存的所有垃圾回收器,在这之前,有必要先了解下垃圾回收的一些基础知识。
GC Root
GC Root又叫根集,它是垃圾回收器扫描存活对象的起始地点。举个简单的例子,如代码清单10-1所示:
代码清单10-1 GC Root示例
public static void test(){
Struct free = new Struct(“a”);
Struct obj = new Struct(“b”);
obj.field1 = new Object();
obj.field2 = 12;System.gc(); // (1)
free = null;
System.gc(); // (2)
}
假设(1)处成功触发垃圾回收,那么垃圾回收器将不能回收任何对象,因为线程栈上包括free和obj引用,它们分别指向对象a和对象b。
在(2)处调用成功后,垃圾回收器可以回收对象a但不能回收对象b,因为栈上存在指向对象b的引用obj,而指向对象a的引用free被赋予null值,即再没有指向对象a的引用,因此对象a被视作垃圾,可回收处理。
代码清单10-1中的free和obj所在的线程栈即GC Root之一,垃圾回收器以它们为起点找到存活对象:凡是从线程栈出发没有触及的对象就可以被认为是死亡对象,继而可以被回收。除了线程栈外,HotSpot VM还有一些地方也可以作为GC Root:
1)所有已加载的类的对象引用(ClassLoaderDataGraph::roots_cld_do);
2)所有线程栈上的对象引用(Threads::possibly_parallel_oops_do);
3)虚拟机内部使用的Java对象引用(Universe::oops_do,SystemDictionary::oops_do);
4)JNI Handle(JNIHandles::oops_do);
5)被synchronized锁住的对象引用(ObjectSynchronizer::oops_do);
6)Java工具用到的对象引用(Management::oops);
7)JVMTI导出对象引用(JvmtiExport::oops_do);
8)AOT堆对象引用(AOTLoader::oops);
9)CodeCache代码引用(CodeCache::blobs_do);
10)String常量池对象引用(StringTable::oops_do)。
安全点
在垃圾回收器的眼中只有垃圾回收线程和修改对象的线程,后者被称为Mutator线程。由于垃圾回收线程也需要修改对象,尤其是在垃圾回收过程中可能有移动对象的情况,如果Mutator线程在移动对象的同时修改对象,势必会造成错误,因此在垃圾回收时一般需要全过程,或者部分过程暂停Mutator线程,这种暂停Mutator线程的现象又叫作世界停顿(Stop The World,STW)。一般来说,Mutator线程可以主动或者被动达到STW,在HotSpot VM中,使用安全点(Safepoint)作为主动STW机制。安全点本质上是一页内存,如代码清单10-2所示:
代码清单10-2 安全点创建
void SafepointMechanism::default_initialize() {
if (ThreadLocalHandshakes) {
...// 分配两页内存,一页用于bad_page,一页用于good_page
char* polling_page = os::reserve_memory(...);
char* bad_page = polling_page;
char* good_page = polling_page + page_size;
// bad_page表示这片内存不可读不可写,good_page表示可读
os::protect_memory(bad_page, page_size, os::MEM_PROT_NONE);
os::protect_memory(good_page, page_size, os::MEM_PROT_READ);
os::set_polling_page((address)(bad_page));
...
} else {
// 分配一页内存
char* polling_page = os::reserve_memory(...);
os::commit_memory_or_exit(...);
// 将它设置为可读
os::protect_memory(polling_page, page_size, os::MEM_PROT_READ);
os::set_polling_page((address)(polling_page));
}
}
虚拟机将“读取安全点内存页”的操作安插在一些合适的地方。当程序没有请求垃圾回收时(实际上除了垃圾回收外,还有其他操作可能会请求安全点),安全点内存页可读,Mutator线程对安全点的访问不会引发任何问题。当需要垃圾回收时,VMThread将安全点设置为不可读不可写,然后等待所有Mutator线程走到安全点。由于Mutator线程访问不可读不可写的内存时会引发异常信号,虚拟机可通过内部的信号处理器捕获并停止Mutator线程的执行,这样一来相当于让所有Mutator线程主动停止。
在具体实现中,SafepointSynchronize::begin()和SafepointSynchronize::end()分别表示安全点的开启和关闭,两者之间构成一个安全区域,它们只能被VMThread调用。代码清单10-3展示了安全点开启的代码实现:
代码清单10-3 SafepointSynchronize::begin
void SafepointSynchronize::begin() {
...
// 设置状态为安全点开启中
_state = _synchronizing;
// 如果使用全局安全点,修改安全点内存页,将其设置为不可读不可写
// (对应还有如果使用线程握手的处理,这里已省略)
if (SafepointMechanism::uses_global_page_poll()) {
Interpreter::notice_safepoints();
PageArmed = 1 ;
os::make_polling_page_unreadable();
}
...
while(still_running > 0) {
jtiwh.rewind();
// 对于当前所有运行的线程
for (; JavaThread *cur = jtiwh.next(); ) {
// 获取线程状态
ThreadSafepointState *cur_state = cur->safepoint_state();// 如果还在运行
if (cur_state->is_running()) {
// 检查线程是不是suspend或者其他情况,并处理它
cur_state->examine_state_of_thread();
// 再次检查线程是否还在运行
if (!cur_state->is_running()) {
// 如果没有运行,计数减一(still_running表示当前还在运行的线程)
still_running--;
}
}
}
... // 如果循环太多次,可能会使当前线程暂停
}
// VMThread等待所有线程停下来
while (_waiting_to_block > 0) {
... Safepoint_lock->wait(true, remaining_time / MICROUNITS);
}
// 安全点开启成功,设置状态,计数增加
_safepoint_counter ++;
_state = _synchronized;
OrderAccess::fence();
... // 日志记录等
}
VMThread会等待所有线程,直到都达到安全点,此时安全点开启成功。开启安全点的核心是线程状态的转换,不同线程进入安全点的方式也不尽相同。
1)解释器线程:第5章提到过,VMThread调用TemplateInterpreter::notice_safepoints通知模板解释器将模板表切换为安全点表(这意味着执行完一条字节码后遇到一个安全点时,可以进入安全点),安全点表除了执行字节码代码外还负责安全点处理,其中就包括进入安全点。
2)执行native代码的线程:VMThread不会暂停执行native代码的线程,但是当线程从native代码返回到Java代码时,需要检查_state,如果发现是_synchronizing则线程停止。
3)执行编译后的代码的线程:开启安全点后,执行编译后的代码的线程使用test指令访问安全点,此时安全点不可读,所以引发异常信号,异常信号会被虚拟机的信号处理器(在Linux平台上是handle_linux_signal)捕获,然后阻塞线程。
4)已经阻塞的线程:对于已经阻塞的线程,继续保持阻塞状态即可,在安全点操作没有结束前不允许醒来。
5)执行虚拟机内部代码或者正在状态转换的线程:Java线程大部分时间在执行字节码,有时也会执行虚拟机自身的一些代码,这些线程会在状态转换时阻塞自身。
线程局部握手
上节节的代码清单10-2展示的代码中有一个线程局部握手(ThreadLocal Handshakes)标志,它是JEP 312引入的特性。根据上面的描述,安全点是一个全局的内存页,一旦VMThread开启安全点(将内存设置为不可读不可写)后,所有Mutator线程都会继续运行直到遇到附近的安全点读取,再通过异常处理机制主动停止。但是有时并不需要停止所有Mutator线程,如偏向锁撤销,或者打印某个线程的线程栈,在这些情况下,VMThread只需要停止某个指定的线程并打印线程栈即可。基于这些考虑,HotSpot VM引入了线程局部握手机制,使VMThread可以有选择性地针对某个线程开启或者关闭线程局部的安全点。
GC屏障
GC屏障即后缀为BarrierSet的一系列类,它们的作用是在字段读操作或者写操作前后插入一段代码,执行某些垃圾回收必要的逻辑,如代码清单10-4所示:
代码清单10-4 GC屏障
public void barrier(Struct obj){
// Write_barreir_pre();
obj.field = new Object();
// Write_barreir_post();
}
虚拟机在字段写操作前后可以分别插入前置写屏障、后置写屏障(读屏障同理),这些写屏障会执行一些GC必要的逻辑,如检测到对象引用关系的修改并记录到记忆集中。GC屏障对性能有较大影响,因为字段读写操作是程序最常见的行为,所以不应该在GC屏障中放置“重量级”代码。
本文给大家讲解的内容是深入解析java虚拟机:垃圾回收,垃圾回收基础概述
- 下篇文章给大家讲解的是深入解析java虚拟机:垃圾回收,Epsilon GC;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!
相关推荐
- Mysql和Oracle实现序列自增(oracle创建序列的sql)
-
Mysql和Oracle实现序列自增/*ORACLE设置自增序列oracle本身不支持如mysql的AUTO_INCREMENT自增方式,我们可以用序列加触发器的形式实现,假如有一个表T_WORKM...
- 关于Oracle数据库12c 新特性总结(oracle数据库19c与12c)
-
概述今天主要简单介绍一下Oracle12c的一些新特性,仅供参考。参考:http://docs.oracle.com/database/121/NEWFT/chapter12102.htm#NEWFT...
- MySQL CREATE TABLE 简单设计模板交流
-
推荐用MySQL8.0(2018/4/19发布,开发者说同比5.7快2倍)或同类型以上版本....
- mysql学习9:创建数据库(mysql5.5创建数据库)
-
前言:我也是在学习过程中,不对的地方请谅解showdatabases;#查看数据库表createdatabasename...
- MySQL面试题-CREATE TABLE AS 与CREATE TABLE LIKE的区别
-
执行"CREATETABLE新表ASSELECT*FROM原表;"后,新表与原表的字段一致,但主键、索引不会复制到新表,会把原表的表记录复制到新表。...
- Nike Dunk High Volt 和 Bright Spruce 预计将于 12 月推出
-
在街上看到的PandaDunk的超载可能让一些球鞋迷们望而却步,但Dunk的浪潮仍然强劲,看不到尽头。我们看到的很多版本都是为女性和儿童制作的,这种新配色为后者引入了一种令人耳目一新的新选择,而...
- 美国多功能舰载雷达及美国海军舰载多功能雷达系统技术介绍
-
多功能雷达AN/SPY-1的特性和技术能力,该雷达已经在美国海军服役了30多年,其修改-AN/SPY-1A、AN/SPY-1B(V)、AN/SPY-1D、AN/SPY-1D(V),以及雷神...
- 汽车音响怎么玩,安装技术知识(汽车音响怎么玩,安装技术知识视频)
-
全面分析汽车音响使用或安装技术常识一:主机是大多数人最熟习的音响器材,有关主机的各种性能及规格,也是耳熟能详的事,以下是一些在使用或安装时,比较需要注意的事项:LOUDNESS:几年前的主机,此按...
- 【推荐】ProAc Response系列扬声器逐个看
-
有考牌(公认好声音)扬声器之称ProAcTablette小音箱,相信不少音响发烧友都曾经,或者现在依然持有,正当大家逐渐掌握Tablette的摆位设定与器材配搭之后,下一步就会考虑升级至表现更全...
- #本站首晒# 漂洋过海来看你 — BLACK&DECKER 百得 BDH2000L无绳吸尘器 开箱
-
作者:初吻给了烟sco混迹张大妈时日不短了,手没少剁。家里有了汪星人,吸尘器使用频率相当高,偶尔零星打扫用卧式的实在麻烦(汪星人:你这分明是找借口,我掉毛是满屋子都有,铲屎君都是用卧式满屋子吸的,你...
- 专题|一个品牌一件产品(英国篇)之Quested(罗杰之声)
-
Quested(罗杰之声)代表产品:Q212FS品牌介绍Quested(罗杰之声)是录音监听领域的传奇品牌,由英国录音师RogerQuested于1985年创立。在成立Quested之前,Roger...
- 常用半导体中英对照表(建议收藏)(半导体英文术语)
-
作为一个源自国外的技术,半导体产业涉及许多英文术语。加之从业者很多都有海外经历或习惯于用英文表达相关技术和工艺节点,这就导致许多英文术语翻译成中文后,仍有不少人照应不上或不知如何翻译。为此,我们整理了...
- Fyne Audio F502SP 2.5音路低音反射式落地音箱评测
-
FyneAudio的F500系列,有新成员了!不过,新成员不是新的款式,却是根据原有款式提出特别版。特别版产品在原有型号后标注了SP字样,意思是SpecialProduction。Fyne一共推出...
- 有哪些免费的内存数据库(In-Memory Database)
-
以下是一些常见的免费的内存数据库:1.Redis:Redis是一个开源的内存数据库,它支持多种数据结构,如字符串、哈希表、列表、集合和有序集合。Redis提供了快速的读写操作,并且支持持久化数据到磁...
- RazorSQL Mac版(SQL数据库查询工具)
-
RazorSQLMac特别版是一款看似简单实则功能非常出色的SQL数据库查询、编辑、浏览和管理工具。RazorSQLformac特别版可以帮你管理多个数据库,支持主流的30多种数据库,包括Ca...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
推荐7个模板代码和其他游戏源码下载的网址
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
-
- Mysql和Oracle实现序列自增(oracle创建序列的sql)
- 关于Oracle数据库12c 新特性总结(oracle数据库19c与12c)
- MySQL CREATE TABLE 简单设计模板交流
- mysql学习9:创建数据库(mysql5.5创建数据库)
- MySQL面试题-CREATE TABLE AS 与CREATE TABLE LIKE的区别
- Nike Dunk High Volt 和 Bright Spruce 预计将于 12 月推出
- 美国多功能舰载雷达及美国海军舰载多功能雷达系统技术介绍
- 汽车音响怎么玩,安装技术知识(汽车音响怎么玩,安装技术知识视频)
- 【推荐】ProAc Response系列扬声器逐个看
- #本站首晒# 漂洋过海来看你 — BLACK&DECKER 百得 BDH2000L无绳吸尘器 开箱
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)