浅谈JVM内存结构
文章目录
JVM内存结构
1.概述
从大角度来看,JVM内存分为两部分,一部分为线程私有的,是我们所不能操作的,还有一部分是所有线程共享的,之后我们对JVM调优也就是调的这一部分。
2.线程私有区
2.1 概述
线程私有部分都是一段连续的内存,速度快,但容量小,用户不可操作。下面是线程私有区的详细图。(现在里面已经有了一个math类)
2.2 程序计数器
一块较小的内存空间,用于标记当前线程所执行字节码的行号。
这一张图片是使用javap -v 对.class文件反汇编出的,图中红色框内的就是程序计数器的标记了。
2.3 虚拟机栈
-
Java 虚拟机栈是由一个个帧栈组成。每个线程都有自己的栈,栈中的数据都是以栈帧的格式存在。
-
栈帧是一个内存区块,是一个数据集维系着方法执行过程中的各种数据信息
-
每个方法执行时会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
-
当方法调用时,栈帧入栈,方法结束时,栈帧出栈。
-
局部变量表的所需的内存空间在编译期间完成分配,运行时不会改变大小。
2.4 本地方法栈
这个不多讲,与java几乎没有什么关系了。
3.线程共享区
3.1概述
线程共享部分分配的内存一般都比较大,因为有垃圾回收机制的影响,所以方法区,堆不是连续的内存。它是用来存东西的。它是我们可以控制的区域,意味着我们之后堆JVM调优就是调这一块。
3.2方法区(Method Area)(永久带/持久带,元空间)
在JDK1.8前,也叫永久带/持久带,JDK1.8后由于oracle把sun收购了,为了整合两款虚拟机,所以改为元空间。
所有定义方法的信息都保存在该区域,静态变量,常量,类信息,运行时常量池都在这个地方。虽然它是一个堆的结构,但为了区别于堆区,所以又一个别名叫非堆(None-heap)
这个不叫堆外内存 (堆外内存不是 JVM 运行时数据区 Runtime Data Area 的一部分,这部分内存区域直接被操作系统管理。
通常我们使用 java.nio.DirectByteBuffer 对象进行堆外内存的管理和使用,它会在对象创建的时候就分配堆外内存。)
3.3堆
3.3.1 概述
几乎所有的对象都放在堆里,当然可以用Native 方法创建堆外内存,把一些对象放到堆外内存中,而堆外内存其实就是方法区。堆是垃圾收集器主要管理的区域
为了方便回收,在堆里面的内存划分用到了分代。分为:
新生代(1/3),老年代(2/3)。新生代下面又分了 Eden,s0(From),s1(To)(这两个在survivor下) 比例为8:1:1.
3.3.2 Eden
西方文化里的伊甸园,人类出生的地方,所以一个新生的对象都会放到Eden区,除非这个对象太大了,太大会直接放到老年代。当Eden区放满了之后,就会进行回收。进行的是youngGC算法
3.3.3 回收算法初体验
回收的话在一般情况下只有两种:youngGC,只回收年轻代,FullGC,全部回收(包括元空间)。当然在CMS收集器中有OldGC。任何虚拟机所有的一个垃圾回收机制都有一个STW(Stop The World),在这个区间内,所有的用户线程停止,专门来执行垃圾回收线程,这也是Java虚拟机想解决,但又解决不了的问题。但youngGC的STW非常很短,短到让人感觉不到,所以一般我们指的STW是FullGC的STW。
3.3.4 survivor
当Eden区触发了垃圾回收,一部分对象被回收,一部分对象幸存下来,幸存下来的对象就开始向survivor区整理,首先是放到s0(From)区域,如果s0区域满了,则再次进行回收,和前面一样,有一部分对象幸存了下来,幸存的对象向s1整理,这是,S1中的对象已经整理好,而S0区已经空了,S1中的对象会全部放到S0区,这样循环往复,每一次循环对象的年龄+1,知道对象的年龄到达15(年龄字段放在对象头中,占4个bit,所以是15),把对象转移到老年代。
3.3.5 老年代
当老年代也放满了就会触发FullGC算法。FullGC执行时间大概是youngGC时间的10倍。随着时间推移,老年代进行一次FullGC所有的对象都存活了下来,就说明里面的对象全部都是有用的,也就是说通过垃圾回收也没有办法解决老年代内存已满的问题,这时就会内存溢出 (OOM Out Of Memory Error),程序结束。