深入理解JVM:内存结构、垃圾收集与性能调优

目录

JDK、JRE、JVM关系?

启动程序如何查看加载了哪些类,以及加载顺序?

class字节码文件10个主要组成部分?

JVM结构

画一下JVM内存结构图

程序计数器

Java虚拟机栈

本地方法栈

Java堆

方法区

运行时常量池?

什么时候抛出StackOverflowError?

例如:

Java7和Java8在内存模型上有什么区别?

程序员最关注的两个内存区域?

直接内存是什么

除了哪个区域外,虚拟机内存其他运行时区域都会发生OutOfMemoryError?

什么情况下会出现堆内存溢出?

空间什么情况下会抛出OutOfMemoryError?

如何设置直接内存容量?

Java堆内存组成?

Edem:from:to默认比例是?

垃圾标记阶段?

引用计数法?

根搜索算法?

JVM中三种常见的垃圾收集算法?

标记-清除算法(Mark_Sweep)

复制算法(Copying)

标记-压缩算法(Mark-Compact)

分代收集算法?

垃圾收集器

Stop The World

Serial收集器

PartNew收集器

Parallel Scavenge

Parallel Old收集器

CMS 收集器

CMS垃圾回收的步骤?

CMS收集器优点?缺点?

G1收集器?

G1收集器是如何改进收集方式的?


JDK、JRE、JVM关系?

Jdk (Java Development Kit):java语言的软件开发包。包括Java运行时环境Jre。

Jre (Java Runtime Environment):Java运行时环境,包括Jvm。

Jvm (Java Virtual Machine) :

  • 一种用于计算机设备的规范。
  • Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虛拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

Jdk包括Jre,Jre包括Jvm。

启动程序如何查看加载了哪些类,以及加载顺序?

Java -XX:+TraceClassLoading 具体类
Java -verbose 具体类

class字节码文件10个主要组成部分?

  • MagicNumber
  • Version
  • Constant_pool
  • Access_flag
  • This_class
  • Super_class
  • Interfaces
  • Fields
  • Methods
  • Attributes

JVM结构

画一下JVM内存结构图

程序计数器

属于线程私有内存。占用一块非常小的空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的指令的字节码,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器来完成。

Java虚拟机栈

属于线程私有内存。它的生命周期与线程相同,虚拟机栈描述的是Java方法执行内存模型;每个方法被执行的时候都会同时创建一个栈桢用于存储局部变量表、操作栈、动态链接、方法出口信息等。每一个方法被调用直至执行完成的过程,就对应着一个栈帧再虚拟机中从入栈到出栈的过程。

本地方法栈

本地方法栈与虚拟机栈所发挥的作用是非常相似的,只不过虚拟机栈对虚拟机执行Java方法服务,而本地栈是为虚拟机使用到Native方法服务。

Java堆

是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
Tps:但随着JIT编译器的发展与逃逸分析技术的逐渐成熟,栈上分配、标亮替换优化技术将会导师一些微妙的变化发生,所有的对象都分配在堆上就不那么绝对了

方法区

是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

运行时常量池?

是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息,还有一项是常量池(Constant PoolTable)用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放道方法区的运行时常量池中。

什么时候抛出StackOverflowError?

如果线程请求的栈深度大于虚拟机所允许的深度,则抛出StackOverflowError。

例如:

public class RecursiveOverflow {  
    public static void main(String[] args) {  
        recursiveMethod();  
    }  
  
    public static void recursiveMethod() {  
        recursiveMethod(); // 无限递归  
    }  
}
  1. 递归调用没有合适的终止条件:当一个方法直接或间接地递归调用自己,并且没有适当的终止条件时,调用栈会不断增长,直到耗尽栈空间。

  2. 方法调用层次过深:即使不是递归调用,但如果方法调用的层次太深(例如,A 方法调用 B 方法,B 方法调用 C 方法,以此类推),也可能导致栈溢出。
  3. 大量的本地变量:虽然这种情况不太常见,但如果在方法中有大量的本地变量(包括对象引用),也可能导致栈帧过大,从而在短时间内耗尽栈空间。
  4. 线程栈大小设置不当:JVM 启动时可以通过 -Xss 参数来设置每个线程的栈大小。如果设置得过小,可能会导致在正常的程序执行过程中就发生栈溢出。

Java7和Java8在内存模型上有什么区别?

  1. Java内存模型(JMM)的改进
    • Java 8引入了新的JMM特性,这些特性主要为了支持更高效的并发编程和多线程应用。JMM定义了一组规则,用于指导程序员编写正确的多线程代码,以避免出现数据竞争和内存可见性问题。
    • Java 8的JMM更加强调了原子性、可见性和有序性。原子性通过volatile关键字和java.util.concurrent.atomic包中的类来实现,确保对变量的操作是不可中断的。可见性确保了一个线程对共享变量的修改对其他线程是可见的。有序性则允许在不影响单线程程序执行结果的情况下对指令进行重排序。
  2. JVM内部变化
    • 方法区变化:Java 8中,方法区(Method Area)的实现由永久代(PermGen)变为了元空间(Metaspace)。这是为了融合HotSpot JVM与JRockit VM而做出的努力,因为JRockit并没有永久代的概念。元空间使用本地内存,而不是虚拟机内存,从而避免了永久代常见的内存溢出问题。
    • 栈和堆的变化:Java 7和Java 8在栈和堆的基本结构上并没有显著变化。Java虚拟机的内存模型中仍然包括Java虚拟机栈、本地方法栈、堆和方法区。Java虚拟机栈和本地方法栈是线程私有的,而堆和方法区是线程共享的。
  3. 内存模型示例代码
    • 尽管Java 7和Java 8在内存模型上有所不同,但使用volatile关键字的示例代码在两者中的表现是相似的。volatile关键字在Java 8中仍然用于确保变量的可见性和禁止指令重排序。

程序员最关注的两个内存区域?

堆(Heap)和栈(Stack),一般大家会把Java内存分为堆内存和栈内存,这是一种比较粗糙的划分方式但实际上Java内存区域是很复杂的。

直接内存是什么

直接内存是Java中用于提高性能的一种重要内存管理方式,特别是在处理大文件读写和NIO操作时。然而,由于它不受JVM的GC管理,使用时需要特别注意内存分配和回收的问题。

  1. 定义
    • 直接内存是指Java堆外内存,即不属于Java虚拟机(JVM)运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。
    • 它属于操作系统内存,不由JVM管理,而是直接由Java应用程序通过直接方式从操作系统中申请。
  2. 特点
    • 分配:直接内存的分配不会受到Java堆大小的限制,但会受到本机总内存的大小及处理器寻址空间的限制。
    • 性能:由于直接内存位于堆外,读写操作可以直接在内存和磁盘之间进行,避免了Java堆和native堆中来回复制数据,从而在某些场景下可以显著提高性能。
    • 回收:直接内存的分配与释放是通过一个Unsafe类型对象进行的(释放通过调用freeMemory),而不是通过Java的垃圾回收(GC)机制。
  3. 用途
    • 常用于NIO(New I/O)操作中,如ByteBuffer的分配。使用直接缓冲区(DirectBuffer)时,操作系统划分出的直接缓存区可以被Java代码直接访问,减少了内核态到用户态的相互拷贝,从而提高了文件读写操作的效率。
    • 学习直接内存的目的是因为Java 8中的元空间的落地方案就是直接内存实现的。
  4. 配置
    • 直接内存大小可以通过MaxDirectMemorySize参数进行设置。如果不指定,则默认与堆的最大值(-Xmx参数值)一致。
  5. 注意事项
    • 由于直接内存在Java堆外,因此它的大小不会直接受限于-Xmx指定的最大堆大小,但是系统内存是有限的,Java堆和直接内存的总和依然受限于操作系统能给出的最大内存。
    • 如果直接内存使用不当,也可能导致OutOfMemoryError异常。

除了哪个区域外,虚拟机内存其他运行时区域都会发生OutOfMemoryError?

程序计数器

什么情况下会出现堆内存溢出?

  1. 对象过多或过大
    • 应用程序创建了过多的对象,并且这些对象在堆内存中占用了大量的空间。
    • 应用程序创建了非常大的对象,超出了堆内存能够容纳的大小。
  2. 内存泄漏
    • 内存泄漏是指应用程序不再需要某些对象,但由于程序中的错误,这些对象没有被垃圾回收器(Garbage Collector, GC)回收,导致它们仍然占用着内存。随着时间的推移,泄漏的对象会越来越多,最终耗尽堆内存。
  3. 静态集合类
    • 静态集合类(如静态HashMap、静态ArrayList等)的生命周期与应用程序的生命周期相同。如果静态集合类中的对象不再需要,但由于它们是静态的,所以垃圾回收器不会回收它们,从而导致内存泄漏。
  4. 缓存
    • 缓存是为了提高性能而存储的常用数据。但是,如果缓存中的数据量过大,或者缓存的清理策略不当,可能会导致堆内存溢出。
  5. 第三方库
    • 某些第三方库可能存在内存管理问题,导致内存泄漏或大量占用内存。
  6. 堆内存设置过小
    • 如果JVM启动参数中设置的堆内存大小(-Xmx)过小,无法满足应用程序的内存需求,也可能导致堆内存溢出。
  7. 无限递归或大量循环
    • 如果程序中存在无限递归或大量循环,每次递归或循环都创建新的对象,那么这些对象会不断占用堆内存,最终导致溢出。
  8. 大对象数组
    • 如果创建了一个大对象数组,并且数组中的每个对象都很大,那么整个数组会占用大量的堆内存。
  9. 死锁
    • 死锁可能会导致线程阻塞,无法继续执行,从而无法释放已经占用的内存资源,间接导致堆内存溢出。
  10. 垃圾回收器配置不当
    • 如果垃圾回收器的配置不当,可能会导致内存回收不及时或回收效率低下,最终导致堆内存溢出。

为了解决堆内存溢出问题,可以采取以下措施:

  • 分析堆转储(Heap Dump)文件,找出哪些对象占用了大量内存,并确定它们是否应该被回收。
  • 优化代码,减少不必要的对象创建和内存占用。
  • 修复内存泄漏问题,确保不再需要的对象能够被垃圾回收器正确回收。
  • 调整JVM启动参数,增加堆内存大小或优化垃圾回收器配置。
  • 使用内存分析工具(如VisualVM、MAT等)来监控和分析应用程序的内存使用情况。

空间什么情况下会抛出OutOfMemoryError?

如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError。

如何设置直接内存容量?

通过 -XX:MaxDirectMemorySize指定,如果不指定,则默认与]ava堆的最大值一样。

Java堆内存组成?

堆大小=新生代+老年代。如果是]ava8则没有PermanentGeneration。
其中新生代(Young)被分为Eden和S0(from)和S1(to)。

Edem:from:to默认比例是?

Edem :from :to=8 :1 :1
此比例可以通过 -XX:SurvivorRatio 来设定

垃圾标记阶段?

在GC执行垃圾回收之前,为了区分对象存活与否,当对象被标记为死亡时,GC才回执行垃圾回收,这个过程就是垃圾标记阶段。

引用计数法?

比如对象a,只要任何一个对象引用了a,则a的引用计数器就加1,当引用失效时,引用计数器就减1,当计数器为0时,就可以对其回收。
但是无法解决循环引用的问题。

根搜索算法?

跟搜索算法是以跟为起始点,按照从上到下的方式搜索被根对象集合所连接的目标对象是否可达(使用根搜索算法后,内存中的存活对象都会被根对象集合直接或间接连接着),如果目标对象不可达,就意味着该对象已经死亡,便可以在 instanceOopDesc的 Mark World 中将其标记为垃圾对象。
在根搜索算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象。

JVM中三种常见的垃圾收集算法?

标记-清除算法(Mark_Sweep)

首先标记出所有需要回收的对象,在标记完成后统一回收掉所有的被标记对象。

缺点:

  • 标记和清除的效率都不高。
  • 空间问题,清除后产生大量不连续的内存随便。如果有大对象会出现空间不够的现象从而不得不提前触发另一次垃圾收集动作。

复制算法(Copying)

他将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

优点:
解决了内存碎片问题。
缺点:
将原来的内存缩小为原来的一半,存活对象越多效率越低。


标记-压缩算法(Mark-Compact)

先标记出要被回收的对象,然后让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。解决了复制算法和标记清理算法的问题。

分代收集算法?

当前商业虚拟机的垃圾收集都采用“分代手机算法",其实就根据对象存活周期的不同将内存划分为几块,一般是新老年代。根据各个年代的特点采用最适当的收集算法。

垃圾收集器

如果说垃圾收集算法是方法论,那么垃圾收集器就是具体实现。连线代表可以搭配使用。

Stop The World

进行垃圾收集时,必须暂停其他所有工作线程,Sun将这种事情叫做"Stop The World"。

Serial收集器

单线程收集器,单线程的含义在于它会 stop the world。垃圾回收时需要stop the world,直到它收集结束。所以这种收集器体验比较差。

PartNew收集器

Serial收集器的多线程版本,除了使用采用并行收回的方式回收内存外,其他行为几乎和Serial没区别。
可以通过选项“-XX:+UseParNewGC"手动指定使用 ParNew收集器执行内存回收任务。

Parallel Scavenge

是一个新生代收集器,也是复制算法的收集器,同时也是多线程并行收集器,与PartNew不同是,它重点关注的是程序达到一个可控制的吞吐量(Thoughput,CPU 用于运行用户代码 的时间/CPU 总消耗时间,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)),高吞吐量可以最高效率地利用CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而不需要太多交互的任务。
他可以通过2个参数精确的控制吞吐量,更高效的利用cpu。

分别是:-XX:MaxCcPauseMillis 和 -XX:GCTimeRatio

Parallel Old收集器

Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。JDK 1.6中才开始提供。

CMS 收集器

Concurrent Mar Sweep 收集器是一种以获取最短回收停顿时间为目标的收集器。重视服务的响应速度,希望系统停顿时间最短。采用标记-清除的算法来进行垃圾回收。

CMS垃圾回收的步骤?
  • 初始标记(stop the world)
  • 并发标记
  • 重新标记(stop the world)
  • 并发清除
    • 初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。
    • 并发标记就是进行Gc Roots Tracing的过程。
    • 重新标记则是为了修正并发标记期间,因用户程序继续运行而导致的标记产生变动的那一部分对象的标记记录,这个阶段停顿时间一般比初始标记时间长,但是远比并发标记时间短。
    • 整个过程中并发标记时间最长,但此时可以和用户线程一起工作。
CMS收集器优点?缺点?

优点:
并发收集、低停顿

缺点:

  • 对cpu资源非常敏感。
  • 无法处理浮动垃圾。
  • 内存碎片问题。

G1收集器?

Garbage First 收集器是当前收集器技术发展的最前沿成果。jdk 1.6_update14中提供了 g1收集器。
G1收集器是基于标记-整理算法的收集器,它避免了内存碎片的问题。
可以非常精确控制停顿时间,既能让使用者明确指定一个长度为 M毫秒的时间片段内,消耗在垃圾收集上的时间不多超过N毫秒,这几乎已经是实时Java(rtsj)的垃圾收集器特征了。

G1收集器是如何改进收集方式的?

极力避免全区域垃圾收集,之前的收集器进行收集的范围都是整个新生代或者老年代。而g1将整个Java堆(包括新生代、老年代)划分为多个大小固定的独立区域,并且跟踪这些区域垃圾堆积程度,维护一个优先级李彪,每次根据允许的收集时间,优先回收垃圾最多的区域。从而获得更高的效率。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

元宇宙对于品牌营销有哪些影响?品牌如何加入?

元宇宙对于品牌营销带来了许多新的营销方式和策略,这些方式在传统营销中是无法实现的。以下是元宇宙对于品牌营销的主要营销方式: 1、虚拟展示: 利用元宇宙技术,品牌可以将产品或服务在虚拟世界中进行展示,用户可以通…

Leetcode - 131双周赛

一,3158. 求出出现两次数字的 XOR 值 本题是一道纯模拟题,直接暴力。 代码如下: class Solution {public int duplicateNumbersXOR(int[] nums) {int ans 0;long t 0;for(int x : nums){if(((t>>x)&1) 1){ans ^ x;}else{t | (…

深度神经网络——什么是迁移学习?

1.概述 在练习机器学习时,训练模型可能需要很长时间。从头开始创建模型架构、训练模型,然后调整模型需要大量的时间和精力。训练机器学习模型的一种更有效的方法是使用已经定义的架构,可能具有已经计算出的权重。这是背后的主要思想 迁移学习…

适合多种苛刻环境的惯性测量单元M-G370PDS

全球IMU市场d在汽车和机器人技术进步和不断增长的应用需求,保持着高速增长的趋势,其中航空航天、国防和汽车等行业对高精度、稳定和紧凑的IMU需求尤为强烈,这些行业对精度和可靠性的高要求直接影响了相关技术的发展方向。 爱普生惯性测量单…

现场辩论赛活动策划方案

活动目的: 技能竞赛中的辩论环节既可以考核员工的知识点,同时也可以考核员工业务办事能力,表达能力,是一种比较全面且较有深度的竞赛方式。 辩论赛细则: 1、时间提示 : 自由辩论阶段,每方使用时间剩…

如何将md文件精确的转换成docx文件

如何将md文件转换成docx? 文章目录 如何将md文件转换成docx?一、如何将MD文件比较完美的转换成word呢?二、方法3 步骤1、下载一个可用的MarkDown编辑器2、下载Pandoc安装 三、来进行转化了 一、如何将MD文件比较完美的转换成word呢&#xff1…

基于51单片机智能蓝牙台灯

基于51单片机智能蓝牙台灯 (仿真+程序+原理图PCB) 功能介绍 具体功能: 1.分为手动/自动两种模式,自动模式下对应LED指示灯亮; 2.手动模式下,可用按键调节亮度; 3.自动…

AI 画图真刺激,手把手教你如何用 ComfyUI 来画出刺激的图

目前 AI 绘画领域的产品非常多,比如 Midjourney、Dalle3、Stability AI 等等,这些产品大体上可以分为两类: 模型与产品深度融合:比如 Midjourney、Dalle3 等等。模型与产品分离:比如 SD Web UI、ComfyUI 等等。 对于…

使用jdk自带jhat工具排查OOM问题

使用jdk自带jhat工具排查OOM问题 OOM java.lang.OutOfMemoryError: Java heap space排查步骤 编写一个测试类 public class TestJVM {Testpublic void test1() throws InstantiationException, IllegalAccessException {List<A> list new ArrayList<>();for (i…

Java开发-面试题-0001-String、StringBuilder、StringBuffer的区别

Java开发-面试题-0001-String、StringBuilder、StringBuffer的区别 更多内容欢迎关注我&#xff08;持续更新中&#xff0c;欢迎Star✨&#xff09; Github&#xff1a;CodeZeng1998/Java-Developer-Work-Note 技术公众号&#xff1a;CodeZeng1998&#xff08;纯纯技术文&am…

OLED显示一张图片

1.思路: void Oled_Show_Image(unsigned char *image) // { unsigned char i; //-128 ~ 127位 unsigned int j; //j要重新定义&#xff0c;因为要到达图片的最后一位 //行 i没有问题&#xff0c;j有问题 i为1时&am…

光速进化!易天万兆光模块全面升级

易天光通信宣布10G SFP/25G SFP28系列光模块产品进行了全新升级&#xff0c;旨在为客户提供更优质、更高效、更可靠的光通信解决方案。这次升级不仅是技术的突破&#xff0c;更是对未来光通信发展趋势的深刻洞察和精准把握。 一、技术革新&#xff0c;性能卓越 本次全系列产品…

强化学习——学习笔记3

一、强化学习都有哪些分类&#xff1f; 1、基于模型与不基于模型 根据是否具有环境模型&#xff0c;强化学习算法分为两种&#xff1a;基于模型与不基于模型 基于模型的强化学习(Model-based RL)&#xff1a;可以简单的使用动态规划求解&#xff0c;任务可定义为预测和控制&am…

windows部署ollama+maxkb+vscode插件continue打造本地AI

windows部署ollamamaxkbvscode插件continue打造本地AI 前言下载ollamadocker desktopvscode插件continue 安装安装ollama设置环境变量 安装docker desktop部署maxkb容器 安装vscode插件模型搜索和推荐 前言 我采用docker运行maxkb&#xff0c;本地运行ollama形式。可能是windo…

HTTP报文

HTTP报文 报文流 HTTP报文是在HTTP引用程序之间发送的数据块&#xff0c;这些数据块以一种文本形式的元信息开头&#xff0c;这些信息描述了报文的内容和含义&#xff0c;后面跟着可选的数据部分&#xff0c;这些报文在客户端&#xff0c;服务器和代理之间流动。 报文流入源…

盘点韩语中的四字成语柯桥留学韩语学习外语培训

일석이조 一石二鸟 일거양득 一举两得 호장성세 虚张声势 새옹15857575376#지마 塞翁失马 간담상조 肝胆相照 이심전심 心心相印 동고동락 同甘共苦 외유내강 外柔内刚 입신양명 扬名立万 다다익선 多多益善 거두절미 截头去尾 일사천리 一泻千里 자유자재 自由自在 탁상공

一套saas模式云MES系统源码,基于springboot+vue.js+uniapp开发

一套saas模式云MES系统源码&#xff0c;基于springbootvue.jsuniapp开发 MES系统简介 MES系统&#xff0c;即制造执行系统&#xff08;Manufacturing Execution System&#xff09;&#xff0c;是一种面向制造企业车间执行层的生产信息化管理系统。它位于上层的企业资源规划&a…

浅谈路由器转发数据包

当路由器转发数据包时&#xff0c;它会经历一系列步骤&#xff0c;包括接收数据包、路由表查询、以及转发数据包。以下是详细的步骤描述&#xff1a; 1. 接收数据包 以太网帧到达端口&#xff1a;当一个以太网帧到达路由器的某个网络接口&#xff08;端口&#xff09;时&#…

通过Transformers用不同的采样方法生成文本

近年来&#xff0c;随着以OpenAI的ChatGPT和Meta的LLaMA为代表的基于数百万网页数据训练的大型Transformer语言模型的兴起&#xff0c;开放域语言生成领域吸引了越来越多的关注。开放域中的条件语言生成效果令人印象深刻&#xff0c;典型的例子有&#xff1a;GPT2在独角兽话题上…

Javascript 基础知识 —— 重写数组方法

1、写一个函数&#xff0c;实现深度克隆对象 const obj {name: "LIYUFAN",age: 25,career: "初级前端工程师",info: {field: ["JS", "CSS", "HTML"],framework: ["React", "Vue", "Angular"…