MangoCool

理解java的内存模型和垃圾回收

2020-11-18 18:56:34   作者:Siva Prasad Rao Janapati   来源:dzone

在本文中,我们将尝试理解 Java 内存模型以及垃圾收集是如何工作的。在本文中,我使用了 JDK8 Oracle Hot Spot 64位 JVM。首先,让我描述一下 Java 进程可用的不同内存区域。

启动 JVM 后,操作系统将为进程分配内存。在这里,JVM 本身是一个进程,分配给该进程的内存包括 Heap、 Meta Space、 JIT 代码缓存、线程堆栈和共享库。我们称之为本机内存。“本机内存”是操作系统提供给进程的内存。操作系统分配给 Java 进程的内存量取决于操作系统、处理器和 JRE。接下来让我解释一下 JVM 可用的不同内存块:

Heap Memory: JVM 用它来存储对象. 这个内存又被划分为两个不同的区域,叫做 "Young Generation Space"和"Tenured Space"

Young Generation: 年轻代或新的空间被分为两个部分称为"Eden Space"和"Survivor Space"。

Eden Space: 当我们创建一个对象,内存将从Eden Space 分配。

Survivor Space: 它包含 Young 垃圾收集或 Minor 垃圾收集中幸存下来的对象。我们有两个等分的 Survivor Space,分别是 S0和 S1。

Tenured Space: 在minor GC 或young GC 期间达到最大终身阈值的对象将被移动到"Tenured Space"或"Old Generation Space"。

当我们讨论垃圾收集过程时,我们将了解如何使用上述内存位置。

Meta Space: 这个内存是堆内存和本机内存以外的一部分。根据文档默认情况,元空间没有上限。在 Java 的早期版本中,我们称之为"Perm Gen Space"。此空间用于存储类装入器加载的类定义。这个设计是为了增长过程中避免出现内存错误。但是,如果增长超过可用物理内存,则操作系统将使用虚拟内存。这将对应用程序性能产生不利影响,因为从虚拟内存到物理内存(反之亦然)进行数据交换是一项代价高昂的操作。我们有 JVM 选项来限制 JVM 使用的元空间。在这种情况下,我们可能会出现内存溢出错误。

Code Cache: JVM有一个解释器来解释字节代码并将其转换为依赖于硬件的机器码。作为JVM优化的一部分,引入了Just In Time (JIT)编译器。经常访问的代码块将被JIT编译为本地代码,并存储在代码缓存中。JIT编译的代码不会被解释。

现在让我们讨论垃圾收集过程。JVM使用一个单独的demon线程来进行垃圾收集。如上所述,当应用程序创建对象时,JVM尝试从 Eden Space 获取所需的内存。JVM执行的GC分为minor GC和major GC。让我们来了解一下minor GC。

最初,survivor space和tenured space是空的。当JVM不能从eden space 获取内存时,它会启动minor GC。在minor GC期间,没有被引用的对象被标记为要收集。JVM选择一个survivor space作为“To Space”。它可以是S0/S1。假设JVM选择了S0作为“To Space”。JVM将对象复制到“to Space”S0,并将对象的年龄增加1。不适合survivor space的对象将被移动到tenured space。这个过程被称为“过早提升”。为了这个图的目的,我把“To Space”设置得比分配的空间大。记住survivor space不会扩大。

在上图中,用红色标记的对象表示它们没有被引用。所有被引用的对象都是 GC roots。垃圾收集器不会删除 GC roots。垃圾回收器删除不可访问的对象并清空eden space。

对于第二次minor GC,垃圾收集器将从“eden space”和“To survivor space (S0)”标记不可到达的对象,将GC roots复制到另一个survivor space S1,并且可到达对象的年龄将增加。

在上面的图中,标记为红色的对象符合GC条件,其他对象将从eden space和survivor space复制到另一个survivor space S1,并且对象的年龄将递增。

对于每个minor GC,上面的过程都会重复。当对象达到最大年龄阈值时,将这些对象复制到tenured space中。

有一个名为“MaxTenuringThreshold”的JVM级别选项,用于指定对象年龄阈值,以将对象提升到保留区空间。默认值是15。

因此,minor GC显然会从“Young Generation Space”中回收内存。minor GC是一个“stop the world”的过程。有些时候,应用程序暂停可以忽略不计。minor GC将根据应用的GC收集器使用单线程或多线程执行。

如果minor GC触发多次,最终“Tenured Space”将被填满,需要进行更多的垃圾收集。在此期间,JVM触发一个“major GC”事件。有时我们称之为full GC。但是,作为full GC的一部分,JVM从“Meta Space”回收内存。如果堆中没有对象,则加载的类将从元空间中删除。
现在让我们看看 JVM 触发器major GC 的可能性:

  1. 如果开发人员调用System.gc()Runtime.getRunTime().gc(),则建议JVM启动GC。
  2. 如果JVM认为没有足够的tenured space
  3. 在minor GC期间,如果JVM不能从eden或survivor spaces回收足够的内存,那么可能会触发major GC。
  4. 如果我们为JVM设置了“MaxMetaspaceSize”选项,并且没有足够的空间装入新类,那么JVM将触发一次major GC。

在接下来的文章中,我们将分析垃圾收集日志以了解如何调优JVM以获得更好的性能。到那时,请继续关注!


原文链接:https://dzone.com/articles/understanding-the-java-memory-model-and-the-garbag

标签: java jvm gc

分享:

上一篇启用和分析垃圾收集日志

下一篇三种思维,让我脱胎换骨

关于我

崇尚极简,热爱技术,喜欢唱歌,热衷旅行,爱好电子产品的一介码农。

座右铭

当你的才华还撑不起你的野心的时候,你就应该静下心来学习,永不止步!

人生之旅历途甚长,所争决不在一年半月,万不可因此着急失望,招精神之萎葸。

Copyright 2015- 芒果酷(mangocool.com) All rights reserved. 湘ICP备14019394号

免责声明:本网站部分文章转载其他媒体,意在为公众提供免费服务。如有信息侵犯了您的权益,可与本网站联系,本网站将尽快予以撤除。