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

csdn推荐

目录

JDK、JRE、JVM关系?

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

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

Jvm (Java Virtual Machine) :

Jdk包括Jre,Jre包括Jvm。

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

Java -XX:+TraceClassLoading 具体类

Java -verbose 具体类

class字节码文件10个主要组成部分? 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(); // 无限递归  
    }  
}

递归调用没有合适的终止条件:当一个方法直接或间接地递归调用自己,并且没有适当的终止条件时,调用栈会不断增长,直到耗尽栈空间。

方法调用层次过深:即使不是递归调用,但如果方法调用的层次太深(例如,A 方法调用 B 方法,B 方法调用 C 方法,以此类推),也可能导致栈溢出。大量的本地变量:虽然这种情况不太常见,但如果在方法中有大量的本地变量(包括对象引用),也可能导致栈帧过大,从而在短时间内耗尽栈空间。线程栈大小设置不当:JVM 启动时可以通过-Xss参数来设置每个线程的栈大小。如果设置得过小,可能会导致在正常的程序执行过程中就发生栈溢出。 Java7和Java8在内存模型上有什么区别? Java内存模型(JMM)的改进: JVM内部变化: 内存模型示例代码: 程序员最关注的两个内存区域?

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

直接内存是什么

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

定义: 特点: 用途: 配置: 注意事项: 除了哪个区域外,虚拟机内存其他运行时区域都会发生OutOfMemoryError?

程序计数器

什么情况下会出现堆内存溢出? 对象过多或过大: 内存泄漏: 静态集合类: 缓存: 第三方库: 堆内存设置过小: 无限递归或大量循环: 大对象数组: 死锁: 垃圾回收器配置不当:

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

空间什么情况下会抛出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垃圾回收的步骤? CMS收集器优点?缺点?

优点:

并发收集、低停顿

缺点:

G1收集器?

Garbage First 收集器是当前收集器技术发展的最前沿成果。jdk 1.6_update14中提供了 g1收集器。

G1收集器是基于标记-整理算法的收集器,它避免了内存碎片的问题。

可以非常精确控制停顿时间,既能让使用者明确指定一个长度为 M毫秒的时间片段内,消耗在垃圾收集上的时间不多超过N毫秒,这几乎已经是实时Java(rtsj)的垃圾收集器特征了。

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

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

文章来源:https://blog.csdn.net/weixin_50108122/article/details/139235011



微信扫描下方的二维码阅读本文

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容