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

news2025/1/12 5:58:35

目录

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堆(包括新生代、老年代)划分为多个大小固定的独立区域,并且跟踪这些区域垃圾堆积程度,维护一个优先级李彪,每次根据允许的收集时间,优先回收垃圾最多的区域。从而获得更高的效率。

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

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

相关文章

SAP_SD模块 物料科目分配/成本简介

SAP系统各模块与财务都有个方面的集成。文本主要说明销售模块中的科目分配和成本的一个对应关系。 1、首先是在物料主数据上销售视图中的物料科目分配组,S1主营、S2材料等字段,物料销售的时候会将这个物料产生的记录到对应的科目中。 首先是物料主数据中…

FreeRTOS【7】队列使用

1.开发背景 操作系统提供了多线程并行的操作,为了方便代码的维护,各个线程都分配了专用的内存并处理对应的内容。但是线程间也是需要协助操作的,例如一个主线程接收信息,会把接收的信息并发到其他线程,即主线程不阻塞&…

数分之SQL查询电商数据案例

1,Python连接SQL数据库 以下是使用Python连接MySQL数据库并进行操作的示例代码: import random import time import pymysql# 定义名字数据 xing ["王", "李", "张", "刘", "陈", "杨", "黄&q…

2024年 云南 融资融券怎么开通,利率多少?4.2

一个小动作,每年节约几万块? 勤俭节约的传统,真的在很多年轻人当中是被嫌弃的,有人要说“吃多了对身体也不好”、“反正食堂饭菜很便宜”之类 但是有效利用资源的观念还是需要培养的。最近了解到很多朋友在券商融资利率很高6%&a…

Pyinstaller打包exe文件解决指南

打包命令 打包 Python 文件 输入如下格式的命令即可 默认命令 Pyinstaller 文件名.py Pyinstaller -option1 -option2 -... 要打包的文件 Pyinstaller 文件名.pyPyinstaller -option1 -option2 -... 要打包的文件 参数选项比较多,这里我列一个表:…

Downie 4 for Mac:视频下载的新选择

对于Mac用户来说,想要轻松下载网上的视频内容,Downie 4无疑是一个绝佳的选择。这款专为Mac打造的视频下载工具,凭借其强大的功能和简洁的操作界面,让视频下载变得轻松又高效。 Downie 4支持从众多网站下载视频,包括各…

LeetCode --- 399周赛

题目列表 3162. 优质数对的总数 I 3163. 压缩字符串 III 3164. 优质数对的总数 II 3165. 不包含相邻元素的子序列的最大和 一、优质数对的总数I 这里由于数据范围比较小,我们可以直接暴力枚举,代码如下 class Solution { public:int numberOfPairs…

STP19NF20 丝印 19NF20 场效应管19A 200V 直插 TO-220

STP19NF20 功率MOSFET的应用领域相当广泛,主要包括: 1. 电源管理:用于高效率电源管理电路,如直流-直流转换器和交流-直流电源适配器。 2. 开关模式电源(SMPS):在需要高效能和紧凑型尺寸的开关…

汽车悬架分为哪几类

汽车悬架分为哪几类 1)汽车的悬架系统可根据结构分为两种:独立悬架和非独立悬架,独立悬架根据构造又可以分为CDC运动悬架(CDC电磁悬架系统)和空气悬架; 2)当前比较火热的空气悬架,是独立悬架的一种; 3)前轮主要使用麦弗逊式独立悬架 和 双叉臂悬架,后轮主要使用多…

本特利330130-040-01-00 PLC模块深度解析 询价联系ID

本特利330130-040-01-00 PLC模块深度解析 在工业自动化领域,准确、高效的数据采集和监控是确保生产安全、提高生产效率的关键。本特利(Bently Nevada)作为全球知名的工业自动化和监控设备制造商,其生产的330130-040-01-00 PLC模块…

实验一 MyBatis框架实验

一、实验环境 Windows10、IDEA2023.1.2、mybatis 3.5.6、DataGr 二、实验目的与要求 1、掌握 MyBatis 开发环境的搭建; 2、熟悉 MyBatis 的开发步骤; 3、掌握 MyBatis 基本对象、配置文件和映射文件的使用; 4、掌握 MyBatis 动态 SQL 开…

基于 DCT 的图像滤波

需求分析 对于图像去噪这一需求,我们可以通过DCT(离散余弦变换)算法来实现。DCT是一种基于频域的变换技术,可以将图像从空间域转换为频域,然后通过滤波等处理方式进行去噪。 针对这一需求,我们需要进行以下…

Android --- Room数据库(Java)

概念 Room 是一个持久性库,属于 Android Jetpack 的一部分。Room 是 SQLite 数据库之上的一个抽象层。SQLite 使用一种专门的语言 (SQL) 来执行数据库操作。Room 并不直接使用 SQLite,而是负责简化数据库设置和配置以及与数据库交互方面的琐碎工作。此…

使用Python类的构造函数和析构函数

1、问题背景 当使用Python类时,可以使用构造函数和析构函数来初始化和清理类实例。构造函数在创建类实例时自动调用,而析构函数在删除类实例时自动调用。 在上面的代码示例中,Person类具有一个构造函数__init__和一个析构函数__del__。构造…

PHP MySQL图解学习指南:开启Web开发新篇章

PHP曾经是最流行的Web开发语言,许多世界领先的网站(如Facebook、维基百科和WordPress)都是用它编写的。PHP运行在Web服务器端,通过使用存储在MySQL数据库中的数据,使得网站可以为每一位访问者显示不同的定制页面。书中采用简单、直观的图示化…

大模型智力升级:AI的未来之路

大模型的发展引领了人工智能的新时代,其强大的数据处理和学习能力在医疗、金融、教育等众多领域取得了令人瞩目的成就。然而,随之而来的挑战也不容忽视。尽管大模型在特定任务上展现出了卓越的性能,但它们在理解复杂语境、处理未见情况的能力…

Linux内网中安装jdk1.8详细教程

本章教程,主要介绍如何在内网环境中配置JDK1.8环境变量 一、下载Linux版压缩包 下载地址:https://www.oracle.com/java/technologies/downloads/#java8 下载完成之后,通过XFTP等工具,将安装包上传到内网服务器 二、安装配置步骤 1、解压压缩包 tar -zxvf /usr/local/jdk-…

[Qt]关于QListWidget、QScrollArea 为什么在QDesigner上设置了之后界面上仍然不生效的问题

前言 最近做了一些有关QListWidget和QScrollArea的控件,我去,这两个控件是真的坑,明明我在QDesigner的操作界面上对这两个控件的界面进行了修改,但是编译出来的软件就是看上去什么都没有,很坑,Gpt也没解决…

html 引用vue3 element 首次加载缩成一团 挤在一起

问题:原生html引用vue,element plus 分析: vue.js, element脚本过大,首次加载网络慢的话,会是缩在这里,没完全渲染 解决: 没加载之前,不显示div,显示一个加载提示语 改动地方app…

CatDDoS僵尸网络与DNSBomb攻击:DDOS攻击最新变种

在近期的网络安全领域,两大严峻挑战浮出水面,为中国乃至全球的网络防护体系敲响了警钟。中国安全厂商奇安信的X实验室团队揭露了一波名为CatDDoS的恶意软件攻击浪潮,与此同时,一种创新且隐蔽的拒绝服务攻击技术——DNSBomb&#x…