耶耶耶耶耶 发表于 2024-9-19 07:55:14

【Java 8】JVM内存架构和GC算法根本分析

https://i-blog.csdnimg.cn/direct/382f036b082b48fcb73f23294e00d177.png

1. 目标

本文讨论了 JDK8 及以上版本的内存管理的根本概念,包罗堆和堆栈内存,以及 GC 及其算法的根本知识。
2. 内存管理的重要性

Java 垃圾收集器不能确保堆内存完全开释,而且对于开辟人员来说,无法欺压垃圾收集器在特定时间运行。因此,了解 Java 中的内存管理如何工作很有帮助。 
了解内存管理有助于编写优化的内存高效代码,并有助于制止程序中任何与内存相关的题目,这些题目可能会导致应用程序运行缓慢,并有助于制止诸如StackOverFlowError  和之类的错误OutOfMemoryError。
3. 堆栈内存

堆栈是一种线性数据结构,是 Java 分配的静态内存,用于存储堆对象引用,还存储 Java 原始范例值。堆栈以后进先出 (LIFO) 顺序访问内存,堆栈比堆内存更快 。
每个线程在内存中创建自己的堆栈,这反过来又使得堆栈内存线程安全。
https://i-blog.csdnimg.cn/direct/4b32953de8e34149a64c855fcc5184b0.png
 Java 中的方法仅访问方法体内(方法作用域内)堆栈内存中的对象。当方法执行完成后,该方法对应的块将从堆栈中清除。 
https://i-blog.csdnimg.cn/direct/c3d956bc6a5f4b778e23b266bdc08f74.png
在上面的程序中,我们可以看到,当控件到达方法时main,堆栈中会有一个条目args。然后当控件位于下一行时,堆栈中会添加一个新条目。 
当控制超出方法的范围时,引用将从堆栈中删除。
https://i-blog.csdnimg.cn/direct/9de56c7a89b34746b03425854a40f3c0.png
如果堆栈内存已满,JVM 将抛出StackOverFlowError。
4. 堆内存

堆用于 JVM 在运行时为 Java 对象分配动态内存。任何新对象都存储在堆中,而reference(example variables) 对象的变量存储在堆栈中。您可以在下面的示例中看到示例代码中的变量如何存储在堆和堆栈中。
int age = 5;
String name = "Sherine";
List<String> subjectLst = new ArrayList<String>();
subjectLst.add("English");
subjectLst.add("Science");
List<String> finalLst;
finalLst = subjectLst;
下面是上述代码片段的堆中的内存分配。
https://i-blog.csdnimg.cn/direct/5195411d610a4f82a90bacfa7db0b676.png
堆内存可以分解为多个较小的部分,称为代, 即年轻代、老生代/终身代 和 永久代。
4.1 年轻代

所有新对象都分配在此内存段中。年轻代由Eden 和两个 Survivor 空间构成。当 Eden 填满时,垃圾收集发生在年轻代,这称为Minor GC 。 在 Minor GC 期间,年轻代中引用的对象将移动到 Survivor 空间 #1,而且对象的年龄会增长。 
比方,在下图中,“对象 1”和“对象 2”将在第一次 Minor GC 后移至 Survivor 空间 #1,而且它们将具有指定的年龄。如果“对象 1”在第一次 Minor GC 后幸存下来,则年龄为零。如今,如果“对象 1”在下一次 Minor GC 后也幸存下来,则它将被移至 Survivor 空间 2,而且年龄将再次增长。
https://i-blog.csdnimg.cn/direct/f3afee24bdf2419a84c17bc7ee846055.png
   图 1:Eden 已满,在 Minor GC 之前
在第二次 Minor GC 期间,位于 Survivor 空间 #1 中的对象(具有引用)将被移动到 Survivor #2,而且年龄将增长(即,年龄将按照示例从零变为一)。而且,所有未引用的来自已满的年轻代空间的对象都将被删除。
https://i-blog.csdnimg.cn/direct/6975bece41b445f68ad033e0ae0243af.png
   图 2:第一次 Minor GC 之后
年轻代的巨细可以通过 来控制NewRatio。如果我们设置,则意味着年轻代和老代/终身代的比例将是 1:4,而 Eden 和 Survivor 空间的总空间将是总堆巨细的 1/5。您还可以利用JVM 选项设置-XX:NewRatio=4年轻代的巨细 。NewSizeMaxNewSize 
4.2 老年代

老年代是存储长寿对象(大多数老龄对象)的地方。年轻代对象有年龄上限或阈值。一旦对象到达该上限,就会将其移至老年代或终身代。
https://i-blog.csdnimg.cn/direct/304313ee5479438e84e712c16629b7e5.png

4.3 长期代

这部分堆内存用于存储运行时类和方法的元数据。Java从JDK 8 开始已完全删除这部分内存,并 用 Metaspace概念取代。您仍然可以设置--XX:PermSize和-XX:MaxPermSize设置。但是,如果您在 JDK 8 或更高版本上运行应用程序,则会在运行时收到告诫。
https://i-blog.csdnimg.cn/direct/562e156f7e044197845d26266fac2952.png
4.4 元空间

这是从 JDK 8 版本开始引入的,它是一个可调整巨细的内存地区,并从本机内存中分配。元空间保存类元数据,它不是连续的内存位置。
每当 Metaspace 到达为其分配的最大巨细时,Java 就会触发自动 GC 来开释 Metaspace 内存。 
元空间选项包罗  -XX:MetaspaceSize=size和-XX:MaxMetaspaceSize=size 
https://i-blog.csdnimg.cn/direct/e7aebf0ca3be4630b477709b92ac3c79.png
5. 垃圾收集

Java 程序编译并转换为字节码,并在 JVM(Java 虚拟机)上运行。Java 程序的对象在该程序的专用堆内存上创建。随着时间的推移,会创建更多对象,而且程序不再需要某些对象(未引用和取消范围的对象)。垃圾收集是 Java 执行自动内存管理并通过删除未引用的对象来开释内存空间的过程。
JVM 采取了不同的垃圾收集算法。垃圾收集算法检查内存中每个引用的对象,别的对象则被视为垃圾收集。
5.1 GC 算法的范例

以下是 JVM 可用的 4 种 GC 算法。


[*]并行 GC 
[*]串行 GC
[*]并发标记和渗透
[*]G1 垃圾优先
5.1.1 并行 GC 

专为具有中等或大量数据的多线程应用程序而设计,在多处置惩罚器环境中运行良好。但它会在垃圾收集期间冻结所有应用程序线程。JVM 选项为-XX:+UseParallelGC,您可以利用 来设置并行线程数-XX:ParallelGCThreads=<NoOfThreads>。 
5.1.2 串行 GC

主要设计用于单线程 环境。与并行 GC 雷同,它还会在垃圾收集期间冻结所有应用程序线程。JVM 选项为-XX:+UseSerialGC。  
5.1.3 并发标记和渗透(CMS)

这是一个并发 GC,旨在紧缩 GC 停息时间,而且不需要制止正在运行的应用程序来执行 GC。这就是为什么与串行或并行 GC 相比,此过程较慢的原因。它利用多线程进行垃圾收集,而且可以与垃圾收集器共享处置惩罚器资源。JVM 选项是-XX:+UseConcMarkSweepGC  
5.1.4 G1垃圾收集器(G1GC)

这是另一种最高效的并发 GC,专为具有大量内存的多处置惩罚器环境而设计。JVM 选项为-XX:+UseG1GC
5.2 选择 GC 算法的参数

除非您对 GC 时间有特定要求并需要添加其他规范,否则最好让 JVM 自行选择 GC 算法。 
如果您想要选择和设置 GC 算法,那么需要考虑一些参数,如堆巨细、CPU 核心数量、应用程序数据集量、吞吐量、停息时间、延迟。 

[*]堆巨细 - 分配给 JVM 的总内存量。堆巨细越大意味着 GC 需要更多时间。此外,与较少的堆内存相比,较大的堆内存意味着 JVM 触发 GC 的频率不会那么高。JVM 选项为-Xms=<n> ,-Xmx=<n> 其中-Xms表示最小值,-Xmx表示最大值。 
[*]CPU 核心 -  GC 算法随 CPU 核心数而变化。其中一些设计用于单核 CPU,一些设计用于多核 CPU。  
[*]应用程序数据集 -这指的是应用程序利用的对象数量。创建更多新对象会导致填满年轻代空间,而且需要更多 GC 时间来开释内存。
[*]吞吐量 -完成应用程序使命所需的总时间(GC 之外)的百分比。它与分配给 JVM 的内存成反比。
[*]停息时间- GC 算法在内存回收期间制止应用程序所花费的时间。它因不同的 GC 算法而异。JVM 选项为-XX:MaxGCPauseMillis=<N>
[*]延迟 - 这是应用程序的响应时间,直接取决于 GC 停息时间。
根据上述参数,您必须选择最适合您应用程序的 GC 算法。比方,


[*]如果应用程序较小且利用较小的数据集,而且在单处置惩罚器上运行而没有停息时间要求,则利用串行 GC。
[*]如果应用程序性能是最高优先级,则利用并行 GC。
[*]当应用程序的响应时间很重要时,请利用 G1GC 或 CMS,因为它不会在运行 GC 时保留应用程序。
检查 JVM 根据您的硬件设置将选择的默认 GC 的一种方法是利用-XX:+PrintCommandLineFlags选项。打开您的命令行选项并运行java -XX:+PrintCommandLineFlags命令。 
上述PrintCommandLineFlags命令根据您运行命令的硬件设置表现 JVM 选择的默认 GC。 
https://i-blog.csdnimg.cn/direct/f2cc5ab61f534eb8b61e1e6bdd9944da.png
6. 总结

以上就java8后面几个版本的jdk的jvm内存架构和GC算法根本知识分享,整理算是比较过细,喜好的小伙伴可以点赞收藏好。



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【Java 8】JVM内存架构和GC算法根本分析