JVM运行时数据区总结

JVM运行时数据区总结

  • JVM简述
    在这里插入图片描述

  • 运行时数据区

在这里插入图片描述

1. JVM内存模型,有哪些区,分别的作用

img

方法区

方法区是用来存储被Java虚拟机加载的类信息,常量,静态变量,运行时常量池等。在jdk8以前,方法区中定义了永久代。因为使用永久代来实现了方法区,所以被描述为堆的一个逻辑部分。但是它确是“非堆”,只是设计堆中的收集器 扩展到了方法区而已。在jdk8的时候,永久代被替换成了元空间(Metaspace)。这样做的好处有以下原因

  • Metaspace使用的是本地内存
  • 字符串常量池存在永久代中,容易出现性能问题和内存溢出。
  • 类和方法的信息难以确定,给永久代指定大小比较困难,太小容易操作永久代溢出,太大会导致老年代溢出。
  • 永久代会为GC带来不必要的复杂性。

java堆

img

Java堆在JVM中是一块最大的内存,用于存储对象实例及数组值,可以认为Java中所有通过new创建的对象的内存都在此分配,堆区由所有线程共享。如上图所示,在堆上划分了很多区域,新生代、老年代和永久代。其中新生代又划分为了一个eden区和两个survivor区。Java堆实际上是垃圾收集器(GC)管理的一块内存区域,经过之上的划分得以进行更有效率的垃圾回收。

虚拟机栈

Java虚拟机栈是线程私有的,每个线程都对应着一个虚拟机栈。每个方法执行的时候都会创建一个栈帧,它是方法运行时的基础数据结构。栈帧中主要存储的有局部变量表、操作数栈、动态链接、方法出口等,当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。虚拟机栈中定义了两种异常,如果线程调用的栈深度大于虚拟机允许的最大深度,则抛出StatckOverFlowError(栈溢出);不过多数Java虚拟机都允许动态扩展虚拟机栈的大小(有少部分是固定长度的),所以线程可以一直申请栈,直到内存不足,此时,会抛出OutOfMemoryError(内存溢出)。

本地方法栈

本地方法栈和虚拟机栈类似,JVM并未要求本地方法栈一定实现某种语言的方法调用,使用者可以灵活去进行调用。用于支持native方法的执行,存储了每个native方法调用的状态。本地方法栈和虚拟机方法栈运行机制一致,它们唯一的区别就是,虚拟机栈是执行Java方法的,而本地方法栈是用来执行native方法的,在很多虚拟机中,会将本地方法栈与虚拟机栈放在一起使用。

程序计数器

程序计数器是一个比较小的内存区域,可能是CPU寄存器或者操作系统内存,其主要用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。 每个程序计数器只用来记录一个线程的行号,所以它是线程私有(一个线程就有一个程序计数器)的。
如果程序执行的是一个Java方法,则计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是一个本地(native,由C语言编写完成)方法,则计数器的值为Undefined,由于程序计数器只是记录当前指令地址,所以不存在内存溢出的情况,因此,程序计数器也是所有JVM内存区域中唯一一个没有定义OutOfMemoryError的区域

2.Java8的内存分代改进

无永久代,类型信息、字段、方法、常量保存在本地内存的元空间,但字符串常量池、静态变量仍在堆

方法区的演进细节
    
首先明确:只有HotSpot才有永久代。
BEA JRockit、IBM J9等来说,是不存在永久代的概念的。原则上如何实现方法区属于虛拟机实现细节,不受《Java虚拟机规范》管束,并不要求统一。
Hotspot中 方法区的变化:
jdk1.6及之前:有永久代(permanent generation) ,静态变量存放在
永久代上
jdk1.7:有永久代,但已经逐步“去永久代”,字符串常量池、静态变量移除,保存在堆中
jdk1.8及之后: 无永久代,类型信息、字段、方法、常量保存在本地内存的元空间,但字符串常量池、静态变量仍在堆

永久代为什么要被元空间替换

随着Java8的到来,HotSpot VM中再也见不到永久代了。但是这并不意味着类.的元数据信息也消失了。这些数据被移到了一个与堆不相连的本地内存区域,这个区域叫做元空间( Metaspace )。
由于类的元数据分配在本地内存中,元空间的最大可分配空间就是系统可用内存空间。
这项改动是很有必要的,原因有:

1)为永久代设置空间大小是很难确定的。
在某些场景下,如果动态加载类过多,容易产生Perm区的O0M。比如某个实际Web工程中,因为功能点比较多,在运行过程中,要不断动态加载很多类,经常出现致命错误。
"Exception in thread' dubbo client x.x connector’java.lang.OutOfMemoryError: PermGenspace"
而元空间和永久代之间最大的区别在于:==元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制==2)对永久代进行调优是很困难的。



StringTable 为什么要调整
jdk7中将StringTable放到了堆空间中。因为永久代的回收效率很低,在full
gc的时候才会触发。而full GC 是老年代的空间不足、永久代不足时才会触发。这就导致了StringTable回收效率不高。而我们开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足。放到堆里,能及时回收内存.

3.Jvm运行时数据区对象什么时候进入老年区

  • 对象年龄达到老年代标准,默认为15

  • 大对象(大小在2KB~128KB),在经过MinorGC后Eden仍然没有足够空间存放,这直接进入老年区

    new的对象先放伊甸园区。此区有大小限制。
    当伊甸园的空间填满时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。再加载新的对象放到伊甸园区
    然后将伊甸园中的剩余对象移动到幸存者0区。
    如果再次触发垃圾回收,此时上次幸存下来的放到幸存者0区的,如果没有回收,就会放到幸存者1区。
    如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区。
    啥时候能去养老区呢?可以设置次数。默认是15次。·可以设置参数:-XX:MaxTenuringThreshold=进行设置。
    在养老区,相对悠闲。当老年区内存不足时,再次触发GC:Major GC,进行养老区的内存清理。
    若养老区执行了Major GC之后发现依然无法进行对象的保存,就会产生OOM异常
    

4.为什么要有新生代和老年代? 新生代中为什么要分为Eden和Survivor?为什么要有两个survivor区?

主要是为了优化GC性能
不同对象的生命周期不同。绝大多数的对象是临时对象。

  • 因为有的对象寿命长,有的对象寿命短。应该将寿命长的对象放在一个区,寿命短的对象放在一个区。不同的区采用不同的垃圾收集算法。寿命短的区清理频次高一点,寿命长的区清理频次低一点。提高效率。

如果没有Survivor区,那么Eden每次满了清理垃圾,存活的对象被迁移到老年区,老年区满了,就会触发Full GC,Full GC是非常耗时的,解决办法:

  • 增加老年代内存,那么老年代清理频次减少,但清理一次花费时间更长。

  • 减少老年代内存,老年代一次FullGC时间更少,频率增加。

都不行,只有再加一层Survivor。将Eden区满了的对象,添加到Survivor区,等对象反复清理几遍之后都没清理掉,再放到老年区,这样老年区的压力就会小很多。即Survivor相当于一个筛子,筛掉生命周期短的,将生命周期长的放到老年代区,减少老年代被清理的次数

先来看一下一个的
在这里插入图片描述
清理内存,很容易产生内存碎片,为了不产生内存碎片,我才用复制算法,将Eden区和Survivor区存活的对象整齐的放到一个空的内存。因为生命周期一般都比较短,所以在存活对象不多的情况下,复制算法效率还是比较高的。

复制算法
在这里插入图片描述
这样就需要一个空内存,而我如果有三个区,这样就总可以保持一个是空的,这样我清理垃圾的时候,就可以将存活对象全部都整齐的放到一个空的内存中,不产生内存碎片了。

5.堆和栈的的区别? 堆的结构?

Java把内存分为栈内存和堆内存。两者的主要区别是:

  • 栈是线程私有,堆是被线程共享的内存区域

  • 作用:虚拟机栈用来描述java执行过程,堆用来存储运行过程中创建的对象和产生的数据

  • 回收:栈只有入栈和出栈没有回收机制,堆有垃圾回收机制

堆的结构:

  • JDK 7以前: 新生区+养老区+永久区(逻辑上,事实非堆)

  • JDK 8以后: 新生区+养老区+元空间

6.Jvm的永久代中会发生垃圾回收吗?

会 ,《Java 虚拟机规范》对方法区的约束是非常宽松的,提到过可以不要求虚拟机在方法区中实现垃圾收集。事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在(如 JDK11 时期的 2GC 收集器就不支持类卸载)。
 一般来说这个区域的回收效果比较难令人满意,尤其是类型的卸载,条件相当苛刻。但是这部分区域的回收有时又确实是必要的。以前 Sun 公司的 Bug 列表中,曾出现过的若干个严重的 Bug 就是由于低版本的 Hotspot 虚拟机对此区域未完全回收而导致内存泄漏。
 方法区的垃圾收集主要回收两部分内容:常量池中废奔的常量和不再使用的类型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值