JVM结构和GC垃圾回收

2023-11-05

JVM知识

一、JVM结构

1、类加载子系统,用于将class字节码文件加载到JVM中

2、JVM运行时数据区,又被称为内存结构

线程共享数据

(1)堆:放对象的地方

(2)方法区(元空间):存储类的模板信息,xxx.class的模板信息,堆中同一个类的对象都指向同一个类模板

线程私有数据

(3)栈:放局部变量、操作数等的地方

(4)本地方法栈:存储本地方法(native)的位置

(5)程序计数器:指向当前线程所执行的字节码指令的地址/行号

堆内存和栈内存,两者的区别? 
 ①创建变量是普通变量,定义时在栈内存中分配,变量在程序运行到作用域外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用new产生数组和对象的语句所在的代码块之外,数组和对象本身占用的堆内存也不会被释放。数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉,这个也是Java比较占内存的主要原因。实际上,栈中的引用变量指向堆内存中的变量,这就是Java中的指针。
 ②通俗来讲:堆是用来存放对象的,而栈是用来执行程序的。
 ③jvm只有一个堆区(heap),被所有线程共享;
 每个线程包含一个栈区(stack),每个栈中的数据都是私有的,其他的栈不能访问,但是同一个栈中变量是共享的;分为:局部变量表、操作数栈、动态链接【这三个是栈帧里面的】、本地方法栈、程序计数器

3、执行引擎:执行代码指令的

JVM的结构图如下

JVM结构

二、程序执行的过程

1、每一个线程在执行Java代码的时候,其都会有一块自己私有的堆(先进后出)、本地方法栈、程序计数器

当线程执行某一个方法A的时候会在私有栈空间中分配一块空间被称为栈帧,当该方法A中又调用了其他方法B,那么该方法A的栈帧会被压栈,然后方法B被分配一块栈帧;当方法被执完之后会出栈;

2、每一个栈帧中都包含了:局部变量表、操作数栈、动态链接、方法出口

局部变量表:局部变量存储的位置

操作数栈:操作数栈可理解为java虚拟机栈中的一个用于计算的临时数据存储区(只是一些基本类型的数据,对象存储在堆里面)。

动态链接:方法中调用其他方法时的地址,一般是需要将方法的符号引用(我理解为方法名被编译后的字符串)转化为其在内存中地址的直接引用,这些符号引用存在于方法区中的运行时常量池

方法出口:方法执行完之后的出口;

main 方int a = 1的执行过程:

首先,在线程的栈中开辟一块空间作为main方法的栈帧,然后将1这个常量压入操作数栈,将a变量存入局部变量表中,然后再将1出栈赋值到局部变量表中的a;

其他加减乘除等数据操作都是先将操作数压入操作数栈,然后执行引擎做运算处理后再出栈赋值给接收的局部变量。


线程私有数据空间

【补充一个小点:javap -c xxx.class生成JVM指令, 后面可以加 > 文件名,可以输出到文件中】

三、堆的解析

内存占比

1、新生代(占堆的1/3)

(1)Eden区(占新生代的8/10)

(2)suivivor区

  • From区(占新生代的1/10)

  • to区(占新生代的1/10)

2、老年代(占堆的2/3)

新创建的对象都是在新生代中创建的。

GC过程:分代回收

(Ⅰ)当Eden区的对象爆满无法再为一个新对象分配内存的时候,就会触发GC垃圾回收>>>minor GC,这种minor GC相对来说是GC里最多的;

在发生minor gc的时候,jvm会对对象进行回收(涉及到eden和suivivor的from),为什么
只涉及eden和from?因为此时to是空的。这里就要牵扯到from和to的转换问题。

(Ⅱ)回收的判断依据是GC ROOT,这里涉及到根搜索算法,java对象的关联关系其实就是一个树形结构,从根到叶子节点遍历,死掉的对象也就和根断开了连接关系,处于游离状态。如果Eden/From区的对象处于游离状态,就是类似于有向图中不可达的节点,没有任何引用指向这个对象,这个时候这个对象就会被抛弃和回收;如果有引用就会将这个对象放到我们的From区,如果我们的From区一批同年龄对象内存占用超过50%,就会把大于这批对象年龄的更老老对象放到老年代中去;

【有人可能会i想,50%就会被回收,那岂不是From区永远都不会满,也就不会出现From区和to区的转换,那to区不就形同虚设了?

如何理解这里的一批同年龄对象呢,举个例子,如果内存为100,现在有40个Age = 1的,35个Age = 2的,剩下25个为Age = 3的,那么GC的时候不会把25个Age = 3的和25个Age = 2的移到老年代,而只是把25个Age = 3移到老年代。

这个意思也就是说,致使From区占用达到50%的那个对象,与他相同年龄的都会被留下来,比这个年龄大的才会被挪走,判断依据是年龄而不是比例】

(Ⅲ)当minor GC再次被触发的时候,那么又会有新的对象被放到From区,这个时候From区之前放进去的对象如果仍然被引用,那么它的Age就会+1(只要触发一次minorGC没被回收的对象Age 就+1),如果Age达到阈值15(默认为15)的时候就会将该对象放到老年代中;

(Ⅳ)如果From区满了,那么此时From区会变为to区,to区会变为From区,然后就把原From区的不被回收的对象拷贝到新的From区中;也就是时刻保持名字为to的堆区为空的,这是由于新生代的GC是采用的复制-清除算法,下面会说原因。

(Ⅵ)此外如果设置了-XX:PretenureSizeThreshold这个参数,那么如果你要创建的对象大于这个参数的值,比如分配一个超大的字节数组,此时就直接把这个大对象放入到老年代,不会经过新生代。

(Ⅶ)老年代爆满的时候会触发full GC,stop-the-world(STW),此时系统停顿不能提供任何服务。

四、关于GC的一些思考

1、为什么Java需要采用分代回收策略呢?

是为了减少full GC,尽可能的让老年代里的对象少一些。

如果不采用分代回收策略的话,新生代一爆满就把对象放到老年代中去,那么老年代很可能很快就爆满了,而老年代里的老不死的对象无处可去就只能fullGC,那么就会导致系统卡顿变慢。

2、为什么会有From区、to区

  • 因为Java的GC采用的是复制算法,那么为什么采用复制算法呢?

(1)首先我们的新生代的对象很容易就挂了要回收,如果不采用复制算法,直接在原位置回收(即标记-清除回收)的话,就可能会导致内存碎片变多,这样的话新生代内存利用率就会下降。

(2)新生代是很活跃的区域,一直有新对象的创建,内存碎片太多就导致实际可用空间变小,那么就会频繁出发垃圾回收,这样会影响系统效率,所以采用复制算法

(3)有的人可能会认为可以不使用复制算法,From区和to区共同使用,两个都用的话可用空间岂不是会更大吗?这样比内存碎片会大更多

个人认为:两个空间都用的话,第一,GC处理速度会变慢,因为不只是考虑一片空间了;第二,时间久了内存碎片也会逐渐增多;第三,任何一个空间满了都只能往老年代里转移,老年代的full GC会增多

  • 那么采用了复制算法又为啥要有两片区域From、to区呢?

(1)既然要使用复制算法,就决定了必须要有两片区域,一篇是源区域,一片是目的区域,把源区域的可用对象赋值到目的区域,并且复制的这个过程把源区域中没用的对象回收掉

五、何时回收对象

1、引用计数算法

给对象添加一个引用计数器,每当有一个地方引用这个对象时,计数器值加1,每当一个引用失效时,计数器减1,任何时刻计数器为0的对象就是不可能再被使用的。在java虚拟机中,并没有使用这个算法来管理内存,其中最主要的原因就是它很难解决对象之间循环引用的问题。

2、可达性分析算法

JVM的主流实现是可达性分析,可达性分析在概念上其实也不难理解,它的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时(图论里面专业一点来说,就是从GC Roots到这个对象不可达),则证明对象是不可用的,大致可以像下图理解。

六、GC算法

1、标记清除

适用于存活对象多的情况,例如老年代,扫描了整个空间两次(第一次:标记存活对象;第二次:清除没有标记的对象)

缺点:产生内存碎片

2、复制算法

从根集合节点进行扫描,标记出所有的存活对象,并将这些存活的对象复制到一块儿新的内存。

现在的商业虚拟机都采用这种收集算法来回收新生代。

适用场合:

存活对象较少的情况下比较高效,存活对象多的话复制成本挺高的。

扫描了整个空间一次(标记存活对象并复制移动)

用于年轻代(即新生代):基本上98%的对象是"朝生夕死"的,存活下来的会很少

3、标记整理

复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的。标记法是在存货对象多的前提下。

标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了一些优化。将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间,为了去除内存碎片。

4、分代收集算法

分代收集算法就是目前虚拟机使用的回收算法,它解决了复制算法不适用于老年代的问题,将内存分为各个年代。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),在堆区之外还有一个代就是永久代(Permanet Generation)。

新生代存活率低,可以使用复制算法。而老年代对象存活率搞,没有额外空间对它进行分配担保,所以只能使用标记清除或者标记整理算法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JVM结构和GC垃圾回收 的相关文章

  • Lambda 性能改进,Java 8 与 11

    我对 lambda 与方法参考运行了一些 JMH 测试 看起来类似于 IntStream reduce Integer max vs IntSream reduce i1 i2 gt Integer max i1 i2 我注意到 在 Jav
  • 估计 64 位 Java 中最大安全 JVM 堆大小

    在分析存在一些问题的 64 位 Java 应用程序的过程中 我注意到分析器本身 YourKit 正在使用真正大量的内存 我在 YourKit 启动脚本中得到的是 JAVA HEAP LIMIT Xmx3072m XX PermSize 25
  • Java 堆被无法访问的对象淹没

    我们的 Java EE 应用程序开始出现一些严重问题 具体来说 应用程序在启动后几分钟内就运行了高达 99 的老年代堆 不会抛出 OOM 但实际上 JVM 没有响应 jstat 显示老年代的大小根本没有减少 没有垃圾收集正在进行 并且kil
  • 如何在JVM不退出的情况下多次运行Java程序?

    假设我有一个Java程序Test class 如果我使用下面的脚本 for i in 1 10 do java Test done JVM每次都会退出java Test被调用 我想要的是跑步java Test在不退出JVM的情况下多次执行
  • 热点 JVM 字节码解释器是跟踪 JIT 吗?

    这个问题几乎说明了一切 我一直在寻找答案 甚至通过 VM 规范 但我没有明确说明 No 不过 还有一些其他 JVM 具有跟踪 JIT HotPath http HotPath GoogleCode Com and Maxine http L
  • 在intellij中为java启用ssl调试

    从我的问题开始 上一期尝试通过 tls ssl 发送 java 邮件 https stackoverflow com questions 39259578 javamail gmail issue ready to start tls th
  • 一个好的 Java VM 中方法调用的开销是多少?

    有人可以提供反汇编的机器代码汇编程序列表吗 我的意思是 与 C 中的普通函数调用相比 肯定有一些开销 VM 需要跟踪调用以查找热点 并且当它使用编译代码时 如果新加载的类需要重新编译 它需要提供动态更改编译方法的方法 我想某处也有返回堆栈溢
  • Scala 中的多个类型下限

    我注意到tuple productIterator总是返回一个Iterator Any 想知道是否无法设置多个下限 因此它可能是最低公共超类型的迭代器 我尝试并搜索了一下 但只发现this https stackoverflow com q
  • 监控 Java 应用程序上的锁争用

    我正在尝试创建一个小基准 在 Groovy 中 以显示几个同步方法上的高线程争用 当监控自愿上下文切换时 应该会出现高争用 在 Linux 中 这可以通过 pidstat 来实现 程序如下 class Res private int n s
  • Scala REPL 中的递归重载语义 - JVM 语言

    使用 Scala 的命令行 REPL def foo x Int Unit def foo x String Unit println foo 2 gives error type mismatch found Int 2 required
  • 尝试使用 Ruby Java Bridge (RJB) gem 时出现错误“无法创建 Java VM”

    我正在尝试实现 Ruby Java Bridge RJB gem 来与 JVM 通信 以便我可以运行 Open NLP gem 我在 Windows 8 上安装并运行了 Java 所有迹象 至少我所知道的 都表明 Java 已安装并可运行
  • JVM:是否可以操作帧堆栈?

    假设我需要执行N同一线程中的任务 这些任务有时可能需要来自外部存储的一些值 我事先不知道哪个任务可能需要这样的值以及何时 获取速度要快得多M价值观是一次性的而不是相同的M值在M查询外部存储 注意我不能指望任务本身进行合作 它们只不过是 ja
  • 调整 Java 类以提高 CPU 缓存友好性

    在设计java类时 对于实现CPU缓存友好性有哪些建议 到目前为止我学到的是应该尽可能多地使用 POD 即 int 而不是整数 这样 在分配包含对象时 数据将被连续分配 例如 class Local private int data0 pr
  • Java中的整数缓存[重复]

    这个问题在这里已经有答案了 可能的重复 奇怪的Java拳击 https stackoverflow com questions 3130311 weird java boxing 最近我看到一个演示 其中有以下 Java 代码示例 Inte
  • 如何在没有 Node.JS 的情况下运行 UglifyJS2

    无论如何都要跑UglifyJS2 https github com mishoo UglifyJS2没有node js 假设我想使用 JavaScript 脚本引擎在 JVM 进程中运行它 怎么做 我看到米秀回答你了https github
  • Java 堆分析因 SIGABRT 崩溃

    我正在尝试分析由 C 编写的方法分配并插入的本机内存JVM通过JNI 我安装了 valgrind version valgrind 3 13 0 并尝试使用以下选项运行 JVM valgrind tool massif massif out
  • 如何制作.Net或JVM语言?

    我看到了 NET 和 JVM 的所有这些新语言 一个人如何开始制作一个 我找不到关于 JVM 或 MSIL 规范的任何好的文档 Edit 我已经知道如何解析 我更感兴趣的是如何有这么多人基于这些平台创建新语言 你有点幸运 为 NET 开发的
  • Java:为什么.class文件中的方法类型包含返回类型,而不仅仅是签名?

    class 文件的常量池中有一个 NameAndType 结构 它用于动态绑定 该类可以 导出 的所有方法都被描述为 签名 返回类型 喜欢 getVector Ljava util Vector 当某些 jar 中方法的返回类型发生更改时
  • Java:VM 如何在 32 位处理器上处理 64 位“long”

    JVM 如何在 32 位处理器上处理 64 位的原始 long 在多核 32 位机器上可以并行利用多个核心吗 64 位操作在 32 位机器上慢了多少 它可能使用多个核心来运行不同的线程 但不会并行使用它们进行 64 位计算 64 位长基本上
  • Java 加载类时如何管理内存?

    想象一下 我有一个包含 10 个方法的类 我需要从该类中实例化 10 个对象 问题是 JVM 会在对象创建时为 10 个实例分配 10 个不同的内存空间 我的意思是在我调用构造函数时 即 new MyClass 吗 或者它会在内存中加载一次

随机推荐

  • XCode14 & iOS16适配 pod签名

    一 iOS16手机开启开发者模式 developer mode disable iOS16手机未打开开发者模式时 1 Xcode 无法选中 iOS16的设备 报错 developer mode disable 2 无法打开升级前编译的App
  • 解决 Axios 跨域问题,轻松实现接口调用

    跨域是指访问另外一个域的资源 由于浏览器的同源策略 默认情况下使用 XMLHttpRequest 和 Fetch 请求时是不允许跨域的 跨域的根本原因是浏览器的同源策略 这是由浏览器对 JavaScript 施加的安全限制 Axios 跨域
  • 使用python简单创建自动点击脚本,使用的是pyautogui

    所有代码在最后面 首先引入包 首先引入包 import pyautogui 鼠标控制包 import time 时间包 后面要用 然后获取需要点击的坐标 for i in range 5 通过循环加延迟 获取鼠标位置 mouse pyaut
  • 推荐 9 个经典前后端分离项目

    前后端分离是现在主流的架构设计模式 它初衷是用 单一职责 原则把代码质量提上去从而达到节省人力和减少沟通时的信息损失的目的 本文推荐九个前后端分离的开源项目 都是采用最流行的技术栈 本文推荐的开源项目已经收录到 Awesome GitHub
  • 黑苹果Mac系统快捷键修改

    由于苹果机的键盘和普通PC机的键盘不同 因此苹果机的快捷键也会与普通PC不同 这对于我们这些经常使用键盘的人来说非常不便 下面附上两者的不同 普通键盘 苹果键盘 修改快捷键 我推荐的软件是KeyBindingsEditor 它很好用 另外需
  • The Evaluation of Language Model (语言模型的性能评价方法 Perplexity)

    The Evaluation of Language Model 语言模型的性能评价 语言模型 Language Model 以下简称LM 直观理解 用于判断一句话是否从语法上通顺 Question 1 训练好的LM效果是好还是坏 如何评价
  • L2-014 列车调度 (25 分)详解

    火车站的列车调度铁轨的结构如下图所示 两端分别是一条入口 Entrance 轨道和一条出口 Exit 轨道 它们之间有N条平行的轨道 每趟列车从入口可以选择任意一条轨道进入 最后从出口离开 在图中有9趟列车 在入口处按照 8 4 2 5 3
  • kali渗透--msf简单使用

    使用MSF Metasploit 利用MS12 020 RDP远程代码执行漏洞 实验环境准备 1 一台 winXP 作为受害者 最好拍摄好一个快照 IP 10 1 1 2 2 kali 作为攻击者 IP 10 1 1 1 3 将攻击者和受害
  • 常用命令图解 & & git 错误 fatal: Not a valid object name: ‘master‘.

    亲测可用 若有疑问请私信 常用命令图解 转自Git 常用命令详解 二 阳光岛主的博客 CSDN博客 git命令 Git 是一个很强大的分布式版本管理工具 它不但适用于管理大型开源软件的源代码 如 linux kernel 管理私人的文档和源
  • Python画各种爱心

    目录 一行代码画爱心 拆解 输出 I U 填充型 动态画红心 桃心 线性 立体红心 玫瑰 树 一行代码画爱心 print n join join Love x y len Love if x 0 05 2 y 0 1 2 1 3 x 0 0
  • 学科竞赛管理系统服务器错误,学科竞赛管理系统

    系统功能模块如下 1 平台首页 整个平台首页分为政策文件 竞赛列表 在线报名 成果展示 通知公告 新闻中心 联系方式 系统登录 下载中心 快速导航等功能模块 所有模块内容全部支持系统后台进行添加 编辑 删除等操作 2 竞赛管理 1 竞赛项目
  • TensorRT/parsers/caffe/caffeParser/caffeParser.h源碼研讀

    TensorRT parsers caffe caffeParser caffeParser h源碼研讀 前言 TensorRT parsers caffe caffeParser caffeParser h delete this std
  • mysql中的枚举enum_mysql中枚举类型之enum详解

    enum类型就是我们常说的枚举类型 它的取值范围需要在创建表时通过枚举方式 一个个的列出来 显式指定 对1至255个成员的枚举需要1个字节存储 对于255至65535个成员 需要2个字节存储 最多允许有65535个成员 先通过sql语句创建
  • Latex 算法Algorithm

    在计算机科学当中 论文当中经常需要排版算法 相信大家在读论文中也看见了很多排版精美的算法 本文就通过示例来简要介绍一下 algorithms 束的用法 该束主要提供了两个宏包 包含两种进行算法排版的环境 algorithm 和 algori
  • java base64转文件_java之文件与base64字符之间的相互转换

    package cn xuanyuan util import java io File import java io FileInputStream import java io FileOutputStream import sun m
  • 大数据 机器学习 分类算法_13种用于数据科学的机器学习分类算法及其代码

    大数据 机器学习 分类算法 The roundup of most common classification algorithms along with their python and r code 吨 他的Roundup与他们的Pyt
  • WSL安装JDK8

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 下载地址 JDK URL https www oracle com technetwork java javase downloads jdk8 downloads 213
  • 压缩感知进阶——有关稀疏矩阵

    上一篇 初识压缩感知Compressive Sensing 中我们已经讲过了压缩感知的作用和基本想法 涉及的领域 本文通过学习陶哲轩对compressive sensing CS 的课程 对压缩感知做进一步理解 针对其原理做出讲解 本文较为
  • poi 导出excel工具类包含导出内容为List<Map<String,Object>>,List<List<Object>>

    导入jar
  • JVM结构和GC垃圾回收

    JVM知识 一 JVM结构 1 类加载子系统 用于将class字节码文件加载到JVM中 2 JVM运行时数据区 又被称为内存结构 线程共享数据 1 堆 放对象的地方 2 方法区 元空间 存储类的模板信息 xxx class的模板信息 堆中同