JVM内存结构
JVM在运行java程序时将运行时数据划分成几种不同格式的数据,分别存储在不同的区域里,这些数据又统一称为运行时数据,而java虚拟机规范又将java运行时数据划分为以下6种.
- pc寄存器
- 栈区
- 堆区
- 方法区
- 运行时常量
- 本地方法栈
严格来说这是一种数据结构,它用于保存当前正常执行的程序的内存地址,同时java程序是多线程执行的,所以需要一个记录员记录下每一个线程执行到哪一条指令.
栈总是和线程关联在一起的,每创建一个线程jvm就会为这个线程创建一个对应的java栈,而每个java栈中又会有多个栈帧,另外有一点需要注意,java栈与java线程是对应起来的,这个数据不是线程共享的,所以我们不用关心它的数据一致性.
堆区是存放java对象的地方,也是开发人员最应该关注的区域,堆区中所有对象都被线程共享,访问时需要关注线程同步问题.
方法区是存放类结构的地方,当class被加载到jvm时会被存放在不同的数据结构中,而其常量,域,方法数据,方法体,构造函数等都存放在此区域.
包括存放编译期数字常量,方法或者域引用,它是方法区一部分,其存储也受方法区规范约束,如果常量池无法分配,也会出现OOM异常.
本地方法栈是为jvm运行native方法准备的空间,由于很多native方法使用c实现,所以也可以称作c栈.
堆区划分
我们程序员最关心的还是Heap区,而对于JVM对堆区的管理又比较复杂,首先我们先看看堆区如何划分.
- Young区
- Old区
- Perm区
Young区分Eden区与两个Survivor区,其中所有全创建的对象都存在Eden区,当Eden区满后会触发Minor GC将Eden区仍然存活的对象复制到Survivor区中,另一个Survivor区中存活的对象也复制到这个Survivor区中,并且保证始终有一个Survivor区是空的.
Old区存放的是Young区的Survivor满后触发Minor GC仍存活的对象,当Eden区满后会将对象存至Survivor区,如果Survivor区仍存不下这些对象,将会把这些对象直接存放至Old区,如果Old区也满了,则会触发Full GC回收整个内存.
PermGen区存放java中的类与类加载器,此外,对以上各区大小也有一定的设置建议,如Young区为整个堆区的1/4,而Survivor区一般为Young区的1/8.
GC收集器
明白堆区如果划分后,我们还需要知道内存是如何分配与回收的,这里我们详细了解下jvm如果回收垃圾对象,这里我们有三种常用垃圾收集算法.
- Serial Collector
- Parallel Collector
- CMS Collector
jvm在client模式下默认GC方式,可以通过JVM参数-XX:+UseSerialGC来指定使用该算法,此时GC是串行,这些动作都是单线程完成,而此时应用程序会全部停止.
其规则很简单,若每次晋升对象都超过了Old区的剩余空间,则说明当前Old区空间已经 不能满足新对象所占用的空间大小,只有触发FullGC才能获得更多内存空间
优点:适合内存有限的情况
缺点:由于使用单线程,回收较慢
根据Minor GC与Full GC的不同分为三种,ParNewGC ParallelGC ParallelOldGC
ParNewGC
使用参数-XX:+UseParNewGC开启,它的对象分配与回收策略与Serial Collector类似,只是回收的线程不是单线程的,而是多线程回收.
ParallelGC
Server默认的GC方式,可以使用-XX:+UseParallelGC开启,并且回收的线程数可以使用-XX:ParallelGCThreads来指定,如果cpu的核数小于8,线程数可以设置与核数一样,如果大于8,一般使用3+(cpu*5)/8
ParallelOldGC
可以使用-XX:+UseParallelOldGC开启,并且回收的线程数可以使用-XX:ParallelGCThreads来指定,同样如果cpu的核数小于8,线程数可以设置与核数一样,如果大于8,一般使用3+(cpu*5)/8
区别
ParallelGC在Full GC操作时会清空整个Heap中的垃圾对象,清除Perm区中已经被卸载的类信息,而ParallelOldGC是消除Heap中部分垃圾对象.
优点:效率高
缺点:当Heap区过大,应用程序暂停时间较长
可以使用-XX:+UseConcMarkSweepGC开启,并发线程数默认为4,当然它也可以使用ParallelCMSThreads来指定大小
CMS GC是不同与Minor GC与Full GC,而是介于这两者之间,触发规则是检查Old区或者Perm区的使用率,当达到一定比例就会触发CMS GC,同时触发时会回收Old区中内存空间,这个值也可以使用-XX:CMSInitiatingOccupancyFraction指定,默认是92%.
同样Perm区默认值也是92%,可通过-XX:CMSInitiatingPermOccupancyFraction指定,需要注意CMS GC只回收Old区与Perm区对象.
优点:Old区回收暂停时间短
缺点:产生内存碎片,整个GC耗时较长,比较耗cpu