最近几个月比较忙,现在项目组忙完了,有一段休息时间,就没事看看java虚拟机相关的内容,经过几天的梳理,将相关的知识记录下来,若中间讲的出现了错误或不足之处,望各位大兄弟给指出来。
JVM大致分为三个区域,栈、方法区、堆,其中栈是线程私有的,不共享,方法区和堆是线程共有的,线程共享,至于为什么,下面将有详细的介绍。
一、栈
栈中主要包括程序计数器、java虚拟机栈、本地方法栈三个模块
1.程序计数器
程序计数器是每个线程都有的,其记录当前线程正在执行的字节码指令的位置。因为java虚拟机的多线程是将线程进行切换,分配给线程不同的cpu执行时间来实现的,因此每个线程都要记录其各自执行指令的位置。所以程序计数器是线程私有的
2.java虚拟机栈
java虚拟机栈是java方法执行时的内存结构,当java方法执行时,虚拟机会为其创建一个“栈帧”,用来存储局部变量表、动态链接、操作数栈、方法的出口,当方法执行结束,该栈帧从java虚拟机栈中出栈。
局部变量表包括基本数据类型和对象的引用。
该区域会出现两种异常,当线程请求的栈深度大于java虚拟机所允许的的深度时,会抛出StackOverFlowError(栈溢出)的异常;若java虚拟机栈可扩展的(一般现在的java虚拟机栈都可动态扩展,但是也存在不可扩展的java虚拟机栈),若扩展时无法申请到足够的内存,则会抛出OutOfMemoryError(内存不足)的异常。
3.本地方法栈
本地方法栈是执行本地方法所创建的本地方法栈。本地方法栈服务的对象是JVM执行的native方法,而虚拟机栈服务的是JVM执行的java方法。例如创建对象时的new就是native方法。我们常用的hotSpot虚拟机已经将本地方法栈和java虚拟机栈合并了。
二、方法区
方法区又称为永久代(PermGen),为了跟堆区分开,又称为“非堆”,但其实只是称呼方法区为永久代,其实其跟永久代还有本质区别,而且永久代还是针对hotSpot虚拟机来讲的,在其他虚拟机上不存在永久代,而且hotSpot在java8时舍弃了永久代,取而代之的是元空间,元空间不在虚拟机上,使用的是本地内存
方法区中存放的是常量、静态变量以及类的信息,从存放的数据就可以看出为什么是线程共享的了,其中类的信息就是类的构造函数、方法、属性。java虚拟机对该区域的规范特别宽松,其内存可以是连续的也可以是不连续的,其大小可以扩展也可以是固定的。
这个区域的内存回收现象是特别少的,但是并不代表进入该区域就跟真的进入永久代一样永远存在,该区域主要发生回收的是常量池,主要是常量的回收以及类型的卸载,但一般该区域的内存回收不是那么理想,而类型的卸载条件更是苛刻。但是该区域的回收还是很必要的,并不能因为难以回收而不回收。
三、堆
堆中存放的是程序运行过程中创建的对象的实例,jvm中垃圾的回收主要集中在该区域,程序运行创建的实例会越来越多,由于硬件条件的限制,需定时清理不再使用的实例,否则会导致内存不足。因此堆和方法区的常量池是jvm进行垃圾回收的主要区域。
堆又分为年轻代(Young Generation)和老年代(Old Generation),年轻代又分为Eden区、Survivor From区、Survivor To区,其中eden:From:To的默认比例是8:1:1,两个Survivor的大小一致,一般来讲老年代要比年轻代大,最底线的情况时两者大小一致,即1:1。
结构图如下:
垃圾回收主要分为Minor GC以及Full GC,Minor GC是年轻代的垃圾回收,Full GC是整个堆的垃圾回收,即对年轻代进行回收,又对老年代进行回收,Minor GC回收的时间短,Full GC回收耗费的时间长。还有一个Major GC,这个不常用
程序中新创建的对象,都在Eden和From区,To区是空的,年轻代的数据几乎都是朝生夕死,该区域回收的概率达到了80%,当进行一次Major GC后,Eden和From区域存活下来的对象都会被复制到To区域,Eden和From清空,此时From和To区域互相调换,From变成To,To变成From,并且将这些对象的年龄设置为1,以后对象在Survivor区每经过一次Minor GC,就将对象的年龄加1,当对象的年龄达到某个值(阈值)时(默认是15岁,使用参数-XX:MaxTenuringThreshold
可以调整)这些对象就会成为老年代。
各个区域设置大小的相应参数:
-Xms:堆的最小空间
-Xmx:堆的最大空间
-Xmn:年轻代的空间
-Xss:每个线程的大小
-XX:NewSize:年轻代最小空间
-XX:MaxNewSize:年轻代的最大空间
-XX:PermSize:持久代的最小空间
-XX:MaxPermSize:持久代的最大空间
没有设置老年代的大小的参数,但是可以通过堆大小和年轻代大小来计算(老年代空间大小=堆空间大小-年轻代空间大小)