JVM和JVM内存管理

     Java虚拟机(Java Virtual Machine 简称JVM)是运行所有Java程序的抽象计算机,是Java语言的运行环境,它是Java 最具吸引力的特性之一。Java源代码经过编译器编译后生成与平台无关的字节码文件(.class文件)。当Java程序需要在某个平台上运行时,JVM负责加载这些字节码文件,并解释或即时编译成对应平台的机器指令执行。JVM使java程序执行具有了“平台无关性”的特点,使java程序实现了“一次编译,到处运行”。

      JVM为java程序提供了内存管理,垃圾回收等服务,并负责安全管理,类加载,字节码验证,以及各种运行时环境的管理的优化工作。JVM对java程序的性能和安全性起着至关重要的作用。

一、内存管理很重要

      在这篇文章中,我们将主要回顾JVM中的内存管理和垃圾回收机制。因为程序在执行过程中可能会消耗大量的内存资源,特别是在处理大量数据、运行复杂算法或维持大量并发操作时。现代应用程序的性能和响应速度往往与内存管理密切相关:

        1. 资源有限:

        无论是服务器还是个人计算机,其物理内存资源都是有限的。如果程序对内存的使用没有有效控制,很容易导致系统内存耗尽,从而引发系统性能急剧下降甚至崩溃。例如,当内存占用过高时,操作系统可能开始频繁地进行页面交换(虚拟内存到硬盘),这会显著降低程序运行速度。

        2. 效率问题:

        良好的内存管理能够确保程序高效利用内存,避免不必要的内存分配和释放,减少内存碎片,提升内存利用率。内存碎片过多会导致可用连续内存空间变小,影响大对象的分配,进一步降低程序运行效率。

        3. 并发安全:

        在多线程环境中,不同的线程共享同一块内存区域,如果没有适当的同步机制和内存管理策略,可能会出现竞态条件和数据不一致的问题,影响程序正确性。

        4. 防止内存泄漏:

        程序中未释放不再使用的内存(即内存泄漏)会持续消耗系统资源,如果不加以管理,长时间运行的程序最终可能导致系统资源枯竭。

        5. 垃圾回收机制的重要性:

        对于像Java这样的语言,其JVM通过垃圾回收机制自动管理内存,它能跟踪并回收不再被引用的对象所占用的内存,大大降低了程序员手动管理内存的复杂度,同时也保证了内存的有效利用。

        因此,内存管理是软件工程中的一个核心环节,不仅关乎着程序能否稳定运行,还直接影响着系统的整体性能和用户体验。

二、JVM如何管理内存?

        JVM在进行内存管理时,主要针对Java堆(Heap)和方法区(Metaspace或Permanent Generation,在JDK 8及更高版本中为Metaspace)以及线程私有的内存区域,如程序计数器、虚拟机栈和本地方法栈进行管理。

1、java堆

        java堆,是java程序中所有线程共享的内存区域,存放java对象实例。堆被分为新生代,老年代,新生代又被分为Eden区,Survivor区(from 和 to)。jvm对每个区域使用不同的垃圾回收算法进行管理。因为新生代对象“朝生夕死”的特点,所以新生代使用复制算法。老年代的对象存活时间较长或者是被多次回收后存活下来的对象,使用标记清除算法或整理算法。

新生代垃圾回收过程:

     1、对象分配:新创建的对象会先被分配到Eden区;

     2、触发MinorGC:Eden区满时(或Eden区没有足够的空间分配给新创建的对象时) 触发MinorGC;

     3、存活对象复制:GC开始时,会扫码Eden区和上次GC仍存活并存在于Survivor区的对象(通常是from)复制到另一个空闲的Survivor的 to区);

     4、晋升老年代:当某个对象经历了多次新生代回收仍然存活(次数可配,通过 JVM 参数 -XX:MaxTenuringThreshold 设置),对象将被移动到老年代。

     5、清除空间:完成复制过程,GC会清除Eden区和Survivor的from区,然后交换两个Survivor区的角色,即原来的to 成为下一次MinorGC 的from区。

     6、调整大小:每个Survivor区的空间大小通常比Eden区小,例如可能是其1/3或者更小的比例,这样可以在有限的内存空间内高效地处理大部分短期生存的对象。

对象什么时候进入老年代?

      1、某个对象经历了多次新生代回收仍然存活。如上4;

      2、大对象直接分配。当对象所占空间超过配置(可通过jvm参数-XX:PretenureSizeThreshold设置),为了避免大对象在新生代多次复制降低效率,大对象将直接被分配到老年代;

      3、动态年龄判断。当Survivor空间不足矣容纳所有应该被保留的对象时,那些“年龄大”的对象即使没有达到配置次数,也会提前进入老年代,以给年轻代腾出空间。

      4、分配担保机制失败。分配担保机制是jvm在进行垃圾回收时的一种策略,用于处理可能出现的内存空间不足的问题。

      在Minor GC前,jvm会判断Survivor区和老年代剩余连续空间是否能容纳从Eden区和from Survivor 区复制过来的存活对象。如果不能容纳,这时候要查看 -XX:HandlePromotionFailure 参数(默认值通常为 true),是否允许担保失败。

      如果允许担保失败,就会继续检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次Minor GC(因为是以历次晋升对象的平均大小为参考,所以这次可能会失败,是有风险的)。如果不允许,或者老年代最大可用连续空间小于历次晋升到老年代对象的平均大小,那么将尽可能将年轻代存活的年龄大的对象或者大对象提前晋升到老年代(即使未达到-XX:MaxTenuringThreshold 或-XX:PretenureSizeThreshold阈值设置).这样能减少Minor GC过程中处理对象的数量,从而降低GC压力,并尽量保证有足够空间存放从新生代晋升过来的对象,进而降低Full GC发生的概率。

老年代的垃圾回收过程:

     老年代垃圾回收的过程通常涉及更复杂的算法和策略,因为它处理的是长期存活的对象。下面以几个常见的老年代垃圾收集器为例简述其工作过程

     CMS(Concurrent Mark Sweep)垃圾收集器

     CMS是一个主要用于老年代的并发标记清除垃圾收集器,其工作流程包含以下四个阶段:

1. 初始标记(Initial Mark):此阶段会暂停所有应用线程,仅标记从GC Roots直接可达的老年代对象以及新生代晋升到老年代的对象。

2. 并发标记(Concurrent Mark):在不暂停应用线程的情况下进行,遍历整个堆并标记所有可到达的老年代对象。由于应用程序持续运行,所以这一步可能会丢失部分垃圾对象的标记。

3. 重新标记(Remark):再次暂停应用线程,修正并发标记阶段因程序继续运行而可能导致的标记错误。

4. 并发清除(Concurrent Sweep):在不暂停应用线程的前提下,清除已被标记为不可达的老年代对象,并且可能进行碎片整理操作,但CMS本身不会做完整的碎片整理,导致空间利用率不高

       G1(Garbage-First)垃圾收集器

       G1垃圾收集器设计用于大规模内存管理它将整个堆划分为多个大小相等的区域(Region),每个区域都可以作为Eden、Survivor或老年代的一部分。G1在回收老年代时采用Stop-the-world模式执行,但尽量减小STW的时间:

1. 并发标记(Initial Mark, Concurrent Mark):与CMS类似,G1首先标记出GC Roots可达的所有对象,然后并发地进行全局标记。

2. 最终标记(Remark):解决并发标记期间因应用线程并发运行导致的引用变动问题,完成精确的标记。

3. 清理(Cleanup):删除已标记为垃圾的对象引用,并统计各个Region中的存活对象分布情况,为后续的混合收集(Mixed GC)做准备。

4. 混合收集(Mixed GC):G1会同时回收年轻代和部分选定的老年代区域,这些区域是根据垃圾回收收益最大化的原则选择的。在回收过程中也会尝试进行压缩和整理,减少内存碎片

      Parallel Old 或 Serial Old 垃圾收集器
     这两种收集器是基于传统的“标记-压缩”算法(Mark-Sweep-Compact)来处理老年代垃圾回收:

     1. 标记:同样需要暂停应用线程,标记出所有存活对象。
     2. 压缩:清除掉所有未被标记的对象,并将剩余存活对象移动到一起,消除内存碎片。
   
       在这些过程中,JVM都会尽可能优化STW(Stop-The-World)停顿时间,确保应用的响应性能。对于老年代而言,由于对象生命周期较长且数量相对较多,因此垃圾回收的成本较高,对性能的影响也较大。不同的收集器通过各种并发和增量的技术来改善这一问题。

2、方法区(元空间 Metaspace)

     方法区在java 8 和更高的版本被元空间替代,用于存储类的元数据信息,如类,方法,常量池,字段描述符等。“元”与开始联系,有“根本”,“本来”的意思。元空间不在堆上分配空间,不归堆内存管理,而是直接在本地操作系统内存上管理,所以其扩展方法和管理方式和堆不同。

     JVM会动态调整Metaspace的大小,当加载的类信息过多导致Metaspace不足时,JVM会触发类卸载操作,并在必要时扩展Metaspace的大小,直至达到最大值(可以通过 -XX:MaxMetaspaceSize 设置)。如果Metaspace达到其最大容量且无法扩展,则会导致 java.lang.OutOfMemoryError: Metaspace 错误。     

    所以,在实际应用中,随着我们工程的扩展和需求迭代,类的元数据信息增加或减少,动态调整 -XX:MaxMetaspaceSize 是十分有必要的,可以有效的帮助我们合理的利用内存空间,并防止内存溢出。并且,因为Java程序在运行过程中,可能动态生成类(如jsp页面转换成Java类,cglib动态代理生成类),加载第三方类库、应用过程中通过反射或classLoader加载额外的类等,我们更需要结合监控预警等手段来监测元空间是否足够。同时,持续优化代码以减少不必要的类加载和卸载也是降低Metaspace占用的有效手段

3、线程私有的内存区域

     其实在之前的文章​​​​​​​JVM-运行时数据区-CSDN博客介绍过线程私有的内存区域,这部分区域与堆的管理方式不同。线程私有的内存区域,通过预设栈大小和以及线程的生命周期栈帧的压栈和出栈自然管理。

     程序计数器:每个线程都有,用于记录当前线程执行的字节码地址,不需特别的内存管理策略;

     虚拟机栈:每个方法调用都会创建一个栈帧,这些栈帧构成了虚拟机栈。栈的空间大小有限,可以通过-Xss参数设置每个线程的栈大小。当线程请求的栈深度超过允许深度的最大值时,将抛出:StackOverFlowError;而如果扩大栈空间不能被满足时,将可能抛出:OutOfMemoryError: Java heap space

     因此,在实际应用中,要注意方法的调用深度!合理的方法深度,不仅关乎代码的可读性,更会影响系统的可用性和可维护性。

   本地方法栈:Java本地方法/Java native方法/JNI_java里的本地库方法-CSDN博客 用于对本地方法的调用,如调用非Java语言的方法或者调用操作系统或硬件的方法,也会像虚拟机栈一样存在 StackOverFlowError和OutOfMemoryError异常,只是不同的JVM对JNI执行的管理不同。

三、总结:

     以上是从内存管理的角度对jvm进行的知识框架梳理,以后在工作中还要多多使用,多多体会。同时,结合已有的工作经验,我更加深刻的认识到,有些时候因为对原理的不了解,研发过程中的一些不经意的"小操作",都可能引起巨大风暴:大量没有被引用的类文件,如果被加载到jvm中,在metaSpaceSize设置的少时,有可能引起OutofMemoryError:Metaspace错误;队列长度设置不当,很有可能导致内存溢出;完善的内存监控系统可以使我们的系统如虎添翼。


关于jvm不同区域的回收机制:

1. Minor GC(年轻代垃圾回收):仅针对新生代(Young Generation)进行的垃圾回收。新生代中的对象大部分是生命周期较短的对象,当新生代空间不足时,就会触发Minor GC。

2. Major GC或者称为 Old GC(老年代垃圾回收):主要针对老年代(Old Generation)进行的垃圾回收,通常在老年代空间不足或发生晋升失败时发生。虽然术语上不如Minor GC常用,但实际上在某些场景下,比如CMS和G1等垃圾收集器中,对老年代的单独回收是可以独立于整个堆的回收存在的。

3.Full GC(全局垃圾回收):是对整个Java堆以及方法区(在Java 8及之前版本为永久代,在Java 9之后为元空间Metaspace)进行全面的垃圾回收。这不仅包括新生代和老年代,还包括其他可能需要回收的内存区域。Full GC的发生频率相对较低,但停顿时间较长,因为它涉及了整个堆的清理和压缩操作。

除此之外,还有特定垃圾收集器特有的回收行为:
Mixed GC(混合垃圾回收):这是G1垃圾收集器特有的回收方式,它会同时回收部分新生代和一部分选定的老年代分区。
Concurrent Mode Failure (CMF):在CMS垃圾收集器中,由于种种原因导致并发阶段无法完成任务而被迫切换到Serial Old模式进行GC,这种情况也会伴随着较大的STW停顿时间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/733532.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【20240309】WORD宏设置批量修改全部表格格式

WORD宏设置批量修改全部表格格式 引言1. 设置表格文字样式2. 设置表格边框样式3. 设置所有表格边框样式为075pt4. 删除行参考 引言 这两周已经彻底变为office工程师了,更准确一点应该是Word工程师,一篇文档动不动就成百上千页,表格图片也是上…

计算机视觉(CV)自然语言处理(NLP)大模型应用,如何实现小模型

在人工智能领域,大模型已经成为引领创新和进步的重要推动力。它们不仅在自然语言处理、计算机视觉等任务中展现了强大的性能,还为各行各业带来了前所未有的机遇和挑战。本文将从一个高级写作专家的角度,深入探讨大模型的现状、技术突破以及未…

STM32之串口中断接收UART_Start_Receive_IT

网上搜索了好多,都是说主函数增加UART_Receive_IT()函数来着,实际正确的是UART_Start_Receive_IT()函数。 —————————————————— 参考时间:2024年3月9日 Cube版本:STM32CubeMX 6.8.1版本 参考芯片&#xff1a…

Svg Flow Editor 原生svg流程图编辑器(二)

系列文章 Svg Flow Editor 原生svg流程图编辑器(一) 说明 这项目也是我第一次写TS代码哈,现在还被绕在类型中头昏脑胀,更新可能会慢点,大家见谅~ 目前实现的功能:1. 元件的创建、移动、形变;2…

完全背包问题(一般写法与空间优化写法)

题目 有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。 第 i 种物品的体积是 vi,价值是 wi。 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。 输入格式 第一行两个整…

最多几个直角三角形python

最多几个直角三角形 问题描述思路代码实现 问题描述 最多可以组成几个直角三角形&#xff0c;一个边只能用一次。 输入描述&#xff1a; 第一行输入一个正整数T&#xff08;1<&#xff1d;T<&#xff1d;100&#xff09;&#xff0c;表示有T组测试数据. 对于每组测试数据…

贪心算法: 奶牛做题

5289. 奶牛做题 - AcWing题库 贝茜正在参加一场奶牛智力竞赛。 赛事方给每位选手发放 n 张试卷。 每张试卷包含 k 道题目&#xff0c;编号 1∼k。 已知&#xff0c;不同卷子上的相同编号题目的难度相同&#xff0c;解题时间也相同。 其中&#xff0c;解决第 i 道题&#xff08;…

【C语言】字符指针

在指针的类型中我们知道有一种指针类型为字符指针char* 一般使用&#xff1a; int main() { char ch w; char *pc &ch; *pc w; return 0; } 还有一种使用方式&#xff0c;如下&#xff1a; int main() { const char* pstr "hello bit.";//这⾥是把⼀个字…

plantUML使用指南之序列图

文章目录 前言一、序列图1.1 语法规则1.1.1 参与者1.1.2 生命线1.1.3 消息1.1.4 自动编号1.1.5 注释1.1.6 其它1.1.7 例子 1.2 如何画好 参考 前言 在软件开发、系统设计和架构文档编写过程中&#xff0c;图形化建模工具扮演着重要的角色。而 PlantUML 作为一种强大且简洁的开…

【stm32 外部中断】

中断&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&#xff0c;处理完成后又返回原来被暂停的位置继续运行 中断优先级&#xff1a;当有多个中…

LoadBalancer (本地负载均衡)

1.loadbalancer本地负载均衡客户端 VS Nginx服务端负载均衡区别 Nginx是服务器负载均衡&#xff0c;客户端所有请求都会交给nginx&#xff0c;然后由nginx实现转发请求&#xff0c;即负载均衡是由服务端实现的。 loadbalancer本地负载均衡&#xff0c;在调用微服务接口时候&a…

考研复习C语言初阶(4)+标记和BFS展开的扫雷游戏

目录 1. 一维数组的创建和初始化。 1.1 数组的创建 1.2 数组的初始化 1.3 一维数组的使用 1.4 一维数组在内存中的存储 2. 二维数组的创建和初始化 2.1 二维数组的创建 2.2 二维数组的初始化 2.3 二维数组的使用 2.4 二维数组在内存中的存储 3. 数组越界 4. 冒泡…

【Java JVM】Class 文件的加载

Java 虚拟机把描述类的数据从 Class 文件加载到内存, 并对数据进行校验, 转换解析和初始化, 最终形成可以被虚拟机直接使用的 Java 类型, 这个过程被称作虚拟机的类加载机制。 与那些在编译时需要进行连接的语言不同, 在 Java 语言里面, 类的加载, 连接和初始化过程都是在程序…

Apache HBase

一、HBase简介 1、HBase定义 Apache HBase™是以hdfs为数据存储的&#xff0c;一种分布式、可扩展的NoSQL数据库。 HBase官网 Welcome to Apache HBase™ Apache HBase™ is the Hadoop database, a distributed, scalable, big data store.Use Apache HBase™ when you nee…

解决阿里云服务器开启frp服务端,内网服务器开启frp客户端却连接不上的问题

解决方法&#xff1a; 把阿里云自带的Alibabxxxxxxxlinux系统 换成centos 7系统&#xff01;&#xff01;&#xff01;&#xff01; 说一下我的过程和问题&#xff1a;由于我们内网的服务器在校外是不能连接的&#xff0c;因此我弄了个阿里云服务器做内网穿透&#xff0c;所谓…

【git】总结

一般提交流程&#xff1a; git add 或者直接加号暂存 git status 检查状态 git commit -m “提交信息” git pull git push origin HEAD:refs/for/分支 git pull 拉代码时有冲突&#xff1a; git add . 暂存所有 git stash 暂存 git pull 解决冲突 git stash apply 放出暂存…

大模型学习过程记录

一、基础知识 自然语言处理&#xff1a;能够让计算理解人类的语言。 检测计算机是否智能化的方法&#xff1a;图灵测试 自然语言处理相关基础点&#xff1a; 基础点1——词表示问题&#xff1a; 1、词表示&#xff1a;把自然语言中最基本的语言单位——词&#xff0c;将它转…

【C语言】C语言编程实战:Base64编解码算法从理论到实现(附完整代码)

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向的学习指导…

你应该打好你的日志,起码避免被甩锅

大家好&#xff0c;我是蓝胖子,相信大家或多或少都有这样的经历&#xff0c;当你负责的功能出现线上问题时&#xff0c;领导第一时间便是找到你询问原因&#xff0c;然而有时问题的根因或许不在你这儿&#xff0c;只是这个功能或许依赖了第三方或者内部其他部门&#xff0c;这个…

PHP端口批量查询工具单文件

查询速度较慢&#xff0c;建议输入20个以内端口&#xff0c;暂未整理端口服务信息所以暂时不显示。 <?php error_reporting(0); //打开报错&#xff1a;去行首双斜杠 header(Content-type:text/html;charset utf-8); //如果乱码删行首双斜杠 header(Cache-Control: no-ca…