详细了解JVM运行时内存( 二 )


详细了解JVM运行时内存

文章插图
本地方法栈特点
  • 本地方法栈加载nativef方法,是为了填补java不方便实现的场景产生的 。
  • 虚拟机栈为为虚拟机执行java服务,而本地方法栈为了执行虚拟机所使用到的native服务
  • 本地方法栈也是线程私有的,和线程的生命周期是一致的,每个线程都有一个本地方法栈 。
4.堆4.1 堆的总括4.1.1 概念Java堆(Java Heap) 是虚拟机所管理的内存中最大的一块 。Java堆是被所 有线程共享的一块内存区域,在虚拟机启动时创建 。此内存区域的唯一目的就是存放对象实例,Java 世界里“几乎”所有的对象实例都在这里分配内存 。
4.1.2 特点
  • 堆是Java虚拟机所管理内存中最大的一块区域 。
  • 堆是线程共享的 。
  • 堆在虚拟机启动的时候创建 。
  • 堆存在的目的就是存放对象实例 。
  • 堆是垃圾回收管理的主要区域 。因此堆又被称作为GC堆,JAVA堆还可以细分为新生代,老年代,永久代(jdk8以后就取消了),其中新生代又分为Eden空间、From survivor、To survivor 。
  • 堆在计算机物理上存储是不连续的,但是逻辑上是连续的,它的大小可以调节(-Xmx,-Xms控制) 。
  • 方法结束后,堆对象不会马上的移除,仅仅在垃圾回收的时候才会移除 。
  • 如果堆中没有足够的内存完成对实例的分配,且堆的空间无法再扩展时,那么将会报出OOM异常 。
4.1.3 设置堆内存大小我们可以通过-Xms来设置最小堆内存,通过-Xmx设置最大堆内存 。
详细了解JVM运行时内存

文章插图
以上是设置了:-Xms5m -Xmx20m
这里可以看出打印出来的Xmx值18m和设置的值20m之间是有差异的,total Memory和最大的内存之间也还是存在比较明显的差异,就是说JVM一般会尽量保持内存在一个尽可能底的层面,而非贪婪做法按照最大的内存来进行分配 。
另外,当我们申请分配内存10m时,我们会发现free Memory和total Memory都上升了,可以看出JVM在内存分配时是动态分配的 。
4.1.4堆的分类JAVA将虚拟机堆分为三个部分:
  • 新生代 (又分为伊甸园区,幸存者区s0和幸存者区s1)
  • 老年代
  • 永久代(JDK1.8后没有了,被本地内存的元空间取代了)
图例如下:
详细了解JVM运行时内存

文章插图

详细了解JVM运行时内存

文章插图
4.2 新生代和老年代4.2.1 对象存储
  • 新生代存放刚创建的实例对象,内存比较小,垃圾回收比较频繁 。新生代又分为Eden区,survivor To区S0和survivor From区S1,其中S0区和S1区并不是固定的from及to的区域,由对象转移的方向决定的,假设对象从S1转移到S0,那么S1便是survivor From,S0是survivor To 。
  • 老年代主要存放一些生命周期比较长的对象,经过在新生代几次的回收依旧没有清除掉,那这部分实例便会转移到老年代 。老年代的垃圾回收相对来讲没有那么频繁 。
4.2.2 配置新生代和老年代的堆中占比默认情况下-XX:NewRatio=2,表示新生代:老年代 = 1:2,新生代占整个堆空间的1/3
案例:假设我们将-XX:NewRatio修改为等于4,那么则表示新生代:老年代 = 1:4,那么新生代占整个堆空间的1/5
除了我们可以配置新生代和老年代的比例之外,我们还可以配置eden和S0和S1在新生代中的占比情况,默认情况下-XX:SurvivorRatio = 8,表示Eden:S0:S1=8:1:1,这表示Eden占整个新生代的8/10,而两个survivor区域分别占了1/10,另外,需要补充一点,由于JVM在运行时,每次都只会使用Eden区和一块survivor区进行服务,因此总是会有一个survivor区域是空闲着的,所以新生代的最高使用也只能达到9/10 。

经验总结扩展阅读