根据JVM内存区域的划分,简单的画了下方的这个示意图。区域主要分为两大块,一块是堆区(Heap),咱们所New出的对象都会在堆区进行分配,在C语言中的malloc所分配的方法就是从Heap区获取的。而垃圾回收器主要是对堆区的内存进行回收的。html
而另外一部分则是非堆区,非堆区主要包括用于编译和保存本地代码的“代码缓存区(Code Cache)”、保存JVM本身的静态数据的“永生代(Perm Gen)”、存放方法参数局部变量等引用以及记录方法调用顺序的“Java虚拟机栈(JVM Stack)”和“本地方法栈(Local Method Stack)”。java
垃圾回收器主要回收的是堆区中未使用的内存区域,并对相应的区域进行整理。在堆区中,又根据对象内存的存活时间或者对象大小,分为“年轻代”和“年老代”。“年轻代”中的对象是不稳定的易产生垃圾,而“年老代”中的对象比较稳定,不易产生垃圾。之因此将其分开,是分而治之,根据不一样区域的内存块的特色,采起不一样的内存回收算法,从而提升堆区的垃圾回收的效率。web
永久代存放JVM运行时使用的类。永久代一样包含了Java SE库的类和方法。永久代的对象在full GC时进行垃圾收集。算法
PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,说说为何会内存益出:这一部分用于存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域,它和和存放Instance的Heap区域不一样,因此若是你的APP会LOAD不少CLASS的话,就极可能出现PermGen space错误。这种错误常见在web服务器对JSP进行pre compile的时候。json
JVM 种类有不少,好比 Oralce-Sun Hotspot, Oralce JRockit, IBM J9, Taobao JVM(淘宝好样的!)等等。固然武林盟主是Hotspot了,这个毫无争议。须要注意的是,PermGen space是Oracle-Sun Hotspot才有,JRockit以及J9是没有这个区域。bootstrap
持久代中包含了虚拟机中全部可经过反射获取到的数据,好比Class和Method对象。不一样的Java虚拟机之间可能会进行类共享,所以持久代又分为只读区和读写区。数组
JVM用于描述应用程序中用到的类和方法的元数据也存储在持久代中。JVM运行时会用到多少持久代的空间取决于应用程序用到了多少类。除此以外,Java SE库中的类和方法也都存储在这里。缓存
若是JVM发现有的类已经再也不须要了,它会去回收(卸载)这些类,将它们的空间释放出来给其它类使用。Full GC会进行持久代的回收。服务器
持久代的大小数据结构
根据上面的各类缘由,永久代最终被移除,方法区移至Metaspace,字符串常量移至Java Heap。
这部份内存空间将所有移除。
JVM的参数:PermSize 和 MaxPermSize 会被忽略并给出警告(若是在启用时设置了这两个参数)。
随着JDK8的到来,JVM再也不有PermGen。但类的元数据信息(metadata)还在,只不过再也不是存储在连续的堆空间上,而是移动到叫作“Metaspace”的本地内存(Native memory)中。
Klass Metaspace和NoKlass Mestaspace都是全部classloader共享的,因此类加载器们要分配内存,可是每一个类加载器都有一个SpaceManager,来管理属于这个类加载的内存小块。若是Klass Metaspace用完了,那就会OOM了,不过通常状况下不会,NoKlass Mestaspace是由一块块内存慢慢组合起来的,在没有达到限制条件的状况下,会不断加长这条链,让它能够持续工做。
元空间的本质和永久代相似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。所以,默认状况下,元空间的大小仅受本地内存限制,但能够经过如下参数来指定元空间的大小:
-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:若是释放了大量的空间,就适当下降该值;若是释放了不多的空间,那么在不超过MaxMetaspaceSize时,适当提升该值。
-XX:MaxMetaspaceSize,最大空间,默认是没有限制的。
除了上面两个指定大小的选项之外,还有两个与 GC 相关的属性:
-XX:MinMetaspaceFreeRatio,在GC以后,最小的Metaspace剩余空间容量的百分比,减小为分配空间所致使的垃圾收集
-XX:MaxMetaspaceFreeRatio,在GC以后,最大的Metaspace剩余空间容量的百分比,减小为释放空间所致使的垃圾收集
-verbose参数是为了获取类型加载和卸载的信息
咱们来看下JVM是如何给元数据分配虚拟内存的空间的
你能够看到虚拟内存空间是如何分配的(vs1,vs2,vs3) ,以及类加载器的内存块是如何分配的。CL是Class Loader的缩写。
要想理解下面这张图,你得搞清楚这些指针都是什么东西。
JVM中,每一个对象都有一个指向它自身类的指针,不过这个指针只是指向具体的实现类,而不是接口或者抽象类。
对于32位的JVM:
_mark : 4字节常量
_klass: 指向类的4字节指针 对象的内存布局中的第二个字段( _klass,在32位JVM中,相对对象在内存中的位置的偏移量是4,64位的是8)指向的是内存中对象的类定义。
64位的JVM:
_mark : 8字节常量
_klass: 指向类的8字节的指针
开启了指针压缩的64位JVM: _mark : 8字节常量
_klass: 指向类的4字节的指针
类指针压缩空间(Compressed Class Pointer Space)
只有是64位平台上启用了类指针压缩才会存在这个区域。对于64位平台,为了压缩JVM对象中的_klass指针的大小,引入了类指针压缩空间(Compressed Class Pointer Space)。
默认是开启
),则UseCompressedOops会使用32-bit的offset来表明java object的引用,而UseCompressedClassPointers则使用32-bit的offset来表明64-bit进程中的class pointer;可使用CompressedClassSpaceSize来设置这块的空间大小查看CompressedClassSpace大小,见《12、jdk工具之jcmd介绍(堆转储、堆分析、获取系统信息、查看堆外内存)》
压缩指针后的内存布局
指针压缩概要
使用-XX:+UseCompressedOops压缩对象指针 "oops"指的是普通对象指针("ordinary" object pointers)。 Java堆中对象指针会被压缩成32位。 使用堆基地址(若是堆在低26G内存中的话,基地址为0)
使用-XX:+UseCompressedClassPointers选项来压缩类指针
对象中指向类元数据的指针会被压缩成32位
类指针压缩空间会有一个基地址
类指针压缩空间只包含类的元数据,好比InstanceKlass, ArrayKlass 仅当打开了UseCompressedClassPointers选项才生效 为了提升性能,Java中的虚方法表也存放到这里 这里到底存放哪些元数据的类型,目前仍在减小
元空间包含类的其它比较大的元数据,好比方法,字节码,常量池等。
元空间的内存管理由元空间虚拟机来完成。先前,对于类的元数据咱们须要不一样的垃圾回收器进行处理,如今只须要执行元空间虚拟机的C++代码便可完成。在元空间中,类和其元数据的生命周期和其对应的类加载器是相同的。话句话说,只要类加载器存活,其加载的类的元数据也是存活的,于是不会被回收掉。
准确的来讲,每个类加载器的存储区域都称做一个元空间,全部的元空间合在一块儿就是咱们一直说的元空间。当一个类加载器被垃圾回收器标记为再也不存活,其对应的元空间会被回收。在元空间的回收过程当中没有重定位和压缩等操做。可是元空间内的元数据会进行扫描来肯定Java引用。
元空间虚拟机负责元空间的分配,其采用的形式为组块分配。组块的大小因类加载器的类型而异。在元空间虚拟机中存在一个全局的空闲组块列表。当一个类加载器须要组块时,它就会从这个全局的组块列表中获取并维持一个本身的组块列表。当一个类加载器再也不存活,那么其持有的组块将会被释放,并返回给全局组块列表。类加载器持有的组块又会被分红多个块,每个块存储一个单元的元信息。组块中的块是线性分配(指针碰撞分配形式)。组块分配自内存映射区域。这些全局的虚拟内存映射区域以链表形式链接,一旦某个虚拟内存映射区域清空,这部份内存就会返回给操做系统。
上图展现的是虚拟内存映射区域如何进行元组块的分配。类加载器1和3代表使用了反射或者为匿名类加载器,他们使用了特定大小组块。 而类加载器2和4根据其内部条目的数量使用小型或者中型的组块。
使用-XX:MaxMetaspaceSize参数能够设置元空间的最大值,默认是没有上限的,也就是说你的系统内存上限是多少它就是多少。-XX:MetaspaceSize选项指定的是元空间的初始大小,若是没有指定的话,元空间会根据应用程序运行时的须要动态地调整大小。
正如前面提到了,Metaspace VM管理Metaspace空间的增加。但有时你会想经过在命令行显示的设置参数-XX:MaxMetaspaceSize来限制Metaspace空间的增加。默认状况下,-XX:MaxMetaspaceSize并无限制,所以,在技术上,Metaspace的尺寸能够增加到交换空间,而你的本地内存分配将会失败。
每次垃圾收集以后,Metaspace VM会自动的调整high watermark,推迟下一次对Metaspace的垃圾收集。
这两个参数,-XX:MinMetaspaceFreeRatio和-XX:MaxMetaspaceFreeRatio,相似于GC的FreeRatio参数,能够放在命令行。
针对Metaspace,JDK自带的一些工具作了修改来展现Metaspace的信息:
示例1:jmap -clstats
[ciadmin@2-103test_app pos-gateway-cloud]$ jmap -clstats 26964 Attaching to process ID 26964, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.131-b11 finding class loader instances ..done. computing per loader stat ..done. please wait.. computing liveness..................................................................................liveness analysis may be inaccurate ... class_loader classes bytes parent_loader alive? type <bootstrap> 2699 4611703 null live <internal> 0x00000000a1013a00 1 880 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a3e931e8 1 880 null dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a083d280 1 1471 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a1c057c8 1 880 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a1013938 1 1474 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a1013d38 1 1471 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a141ae78 1 880 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a083d1b8 1 1473 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a163c658 1 880 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a293afa8 1 1473 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a19ec0a0 15 70893 0x00000000a001b938 live com/aliyun/openservices/shade/com/alibaba/fastjson/util/ASMClassLoader@0x000000010066b7a0 0x00000000a2778848 1 1474 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a141a900 1 880 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a083d8c0 1 1473 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 0x00000000a163c720 1 880 0x00000000a001b938 dead sun/reflect/DelegatingClassLoader@0x0000000100009df8 ... 0x00000000a094fe68 0 0 0x00000000a0007438 live java/net/URLClassLoader@0x000000010000ecd0 total = 177 12836 20539140 N/A alive=9, dead=168 N/A [ciadmin@2-103test_app pos-gateway-cloud]$
示例二:jstat -gc 26964
[ciadmin@2-103test_app pos-gateway-cloud]$ jstat -gc 26964 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 3072.0 3072.0 2384.8 0.0 62976.0 6699.1 445440.0 67911.5 69760.0 68124.8 8320.0 7929.0 3792 36.649 12 1.971 38.620 [ciadmin@2-103test_app pos-gateway-cloud]$
示例三:jcmd 5943 GC.class_stats
[ciadmin@2-103test_app pos-gateway-cloud]$ jcmd 5943 GC.class_stats 5943: GC.class_stats command requires -XX:+UnlockDiagnosticVMOptions [ciadmin@2-103test_app pos-gateway-cloud]$
说是:应用程序启动时增长-XX:+UnlockDiagnosticVMOptions参数
加了上面的参数后,从新来一把以下:
D:\workspace\study\target\classes\com\dxz\jvm>jcmd 4332 GC.class_stats 4332: Index Super InstBytes KlassBytes annotations CpAll MethodCount Bytecodes MethodAll ROAll RWAll Total ClassName 1 -1 258458040 480 0 0 0 0 0 24 584 608 [Ljava.lang.Object; 2 368 217343856 1000 0 6864 51 3955 13744 8664 13888 22552 java.util.HashMap 3 368 144895744 1432 0 15584 93 9536 37136 19104 37048 56152 java.util.concurrent.ConcurrentHashMap 4 363 99615560 928 0 8232 24 1719 6040 4392 11304 15696 java.net.URLClassLoader 5 -1 90560256 480 0 0 0 0 0 32 584 616 [Ljava.util.concurrent.ConcurrentHashMap$Node; 6 -1 90559840 480 0 0 0 0 0 32 584 616 [Ljava.util.WeakHashMap$Entry; 7 367 72447872 1384 0 5288 59 2245 13544 7080 14016 21096 java.util.Vector 8 367 54335928 1320 0 4936 49 2359 12104 6600 12592 19192 java.util.ArrayList 9 368 54335904 976 0 4952 32 1845 11968 4856 13744 18600 java.util.WeakHashMap 10 14 54335856 656 0 7488 33 1513 8504 4792 12496 17288 sun.misc.URLClassPath 11 14 45280240 504 0 4960 27 2551 11792 5232 12536 17768 java.security.AccessControlContext 12 14 45279920 528 0 4328 12 1024 3760 2488 6496 8984 java.security.ProtectionDomain 13 14 36226208 568 0 1344 8 223 1744 1024 2952 3976 java.util.concurrent.ConcurrentHashMap$Node 14 -1 36224752 496 0 1144 14 109 2520 1112 3272 4384 java.lang.Object 15 14 36224032 552 0 1840 7 410 2744 1288 4160 5448 java.lang.ref.ReferenceQueue 16 14 36223936 552 0 5320 14 1796 4648 3552 7328 10880 java.security.CodeSource 17 7 36223936 1424 0 864 6 88 1664 704 3552 4256 java.util.Stack 18 373 27167952 1008 0 808 4 69 1000 592 2528 3120 java.util.Collections$SynchronizedSet 19 -1 27167880 480 0 0 0 0 0 24 584 608 [Ljava.security.ProtectionDomain; 20 14 18112048 496 0 360 2 10 920 216 1720 1936 java.lang.ref.ReferenceQueue$Lock 21 -1 18111968 480 0 0 0 0 0 24 584 608 [Ljava.security.Principal; ... 484 14 0 496 0 1416 20 737 3736 2240 3680 5920 sun.util.locale.LocaleUtils 1535868936 298000 1536 955600 6934 264621 1445736 885528 1980368 2865896 Total 53591.2% 10.4% 0.1% 33.3% - 9.2% 50.4% 30.9% 69.1% 100.0% Index Super InstBytes KlassBytes annotations CpAll MethodCount Bytecodes MethodAll ROAll RWAll Total ClassName D:\workspace\study\target\classes\com\dxz\jvm>
若是你理解了元空间的概念,很容易发现GC的性能获得了提高。
java8中metaspace总结以下:
这部份内存空间将所有移除。
JVM的参数:PermSize 和 MaxPermSize 会被忽略并给出警告(若是在启用时设置了这两个参数)。
大部分类元数据都在本地内存中分配。
用于描述类元数据的“klasses”已经被移除。
默认状况下,类元数据只受可用的本地内存限制(容量取决因而32位或是64位操做系统的可用虚拟内存大小)。
新参数(MaxMetaspaceSize)用于限制本地内存分配给类元数据的大小。若是没有指定这个参数,元空间会在运行时根据须要动态调整。
对于僵死的类及类加载器的垃圾回收将在元数据使用达到“MaxMetaspaceSize”参数的设定值时进行。
适时地监控和调整元空间对于减少垃圾回收频率和减小延时是颇有必要的。持续的元空间垃圾回收说明,可能存在类、类加载器致使的内存泄漏或是大小设置不合适。
前面已经提到,元空间虚拟机采用了组块分配的形式,同时区块的大小由类加载器类型决定。类信息并非固定大小,所以有可能分配的空闲区块和类须要的区块大小不一样,这种状况下可能致使碎片存在。元空间虚拟机目前并不支持压缩操做,因此碎片化是目前最大的问题。