关于JVM内存的N个问题

今日头条 · 2019-03-27
作者:melonstreet
来历:http://www.cnblogs.com/QG-whz/p/9636366.html


JVM的内存区域是怎样区分的?

JVM的内存区分中,有部分区域是线程私有的,有部分是归于整个JVM进程;有些区域会抛出OOM反常,有些则不会,了解JVM的内存区域区分以及特征,是定位线上内存问题的根底。那么JVM内存区域是怎样区分的呢?

首要是程序计数器(Program Counter Re丁燕桃gister),在JVM标准中,每个线程都有自己的程序计数器。这是一块比较小的内存空间,存储当时线程正在履行的Java办法的JVM指令地址,即字节码的行号。假如正在履行Native办法,则这个计数器为空。该内存区域是仅有一个在Java虚拟机标准中没有规矩任何OOM状况的内存区域。

第二,Java虚拟机栈(Java Virtal Machine Stack),相同也是归于线程私有区域,每个线程在创立的时分都会创立一个虚拟机栈,生命周期与线程共同,线程退出时,线程的虚拟机栈也蒋大为状告五环之歌收回。虚拟机栈内部坚持一个个的栈帧,每次办法调用都会进行压栈,JVM对栈帧的操作只要出栈和压栈两种,办法调用完毕时会进行出栈操作。

该区毛家超张黎山歌全集域存储着局部变量表,编译时期可知的各种根本类型数据、目标引证、办法出口等信息。

第三,本地办法栈(Native Method Stack)与虚拟机栈相似,本地办法栈是在调用本地办法时运用的栈,每个线程都有一个本地办法栈。

第四,堆(Heap),简直一切创立的Java目标实例,都是被直接分配到堆上的。堆被一切的线程所同享,在堆上的区域,会被废物收回器做进一步区分,例如重生代、老时代的区分。Java虚拟机在发动的时分,能够运用“黄梦晨Xmx”之类的参数指定票预安堆区域的巨细。

第五,办法区(Method Area)。办法区与堆相同,也是一切的线程所同享,存储被虚拟机加载的元(Meta)数据,包含类信息、常量、静态变量、即时编译器编鬼刀冰公主译后的代码等数据。这儿需求留意的是运转时常量池也在办法区中。依据Java虚拟机标准的规矩,当办法区无法满意内存分配需求时,将抛出OutOfMemoryError反常。因为前期HotSpot JVM的完结,将GC分代搜集拓宽到了办法区,因而许多人会将办法区称为永久代。Oracle JDK8中已永久代移除永久代,一起增加了元数据区(Metaspace)。

第六,运转时常量池(Run-Time Constant Povypr官网ol),这是办法区的一部分,遭到办法区内存的约束,当常量池无法再申请到内存时,会抛出OutOfMemoryError反常。

在Class文件中,除了有类的版别、办法、字段、接口等描绘信息外,还有一项信息是常量池。每个Class文件的头四个字节称为Magic Number,它的效果是确认这是否是一个能够被虚拟机承受的文件;接着的四个字节存储的是Class文件的版别号。紧挨着版别号之后的,便是常量池进口了。常量池首要寄存两大类常量:

  • 字面量(Literal),如文本字符串、final常量值
  • 符号引证,寄存了与编译相关的一些常量,因为Java不像C++那样有衔接的进程,因而字段办法这些符号引证在运转期就需求进行转化,以便得到真实的内存进口地址。

class文件中的常量池,也称为静态常量池,JVM虚拟机完结类装载操作后,会把静态常量池加载到内存中,寄存在运转时常量池。

第七,直接内存(Direct Memory),直接内存并不归于Java标准规矩的归于Java虚拟机运转时数据区的一部分。Java的NIO能够运用Native办法直接在java堆外分配内存,运用DirectByteBuffer目标作为这个堆外内存的引证。

下面这张图,反映了运转中的Java进程内存占用状况:

OOM或许我在索债公司这些年发作在哪些区域上?

依据javadoc的描绘,OOM是指JVM的内存不行用了,同关于JVM内存的N个问题时废物搜集器也无法供给更多的内存。从描绘中能够看出,在JVM抛出OutOfMemoryError之前,废物搜集器一般会出马先测验收回内存。

从上面剖析的Java数据区来看,除了程序计数器不会发作OOM外,哪些区域会发作OOM的状况呢?

榜首utsonline,堆内关于JVM内存的N个问题存。堆内存不足是最常见的发送OOM的原因之一,假如在堆中没有内存完结目标实例的分配,而且堆无法再扩展时,将抛出OutOfMemoryError反常。当时干流的JVM能够经过-Xmx和-Xms来操控堆内存的妈妈相片巨细,发作堆上OOM的或许是存在内存走漏,也或许是堆巨细分配不合理。

第二,Java虚拟机栈和本地办法栈,这两个区域的差异不过是虚拟机栈为虚拟机履行Java办法效劳,而本地办法栈则为虚拟机运用到的Native办法效劳,在内存分配反常上是相同的。在JVM标准中,对Java虚拟机栈规矩了两种反常:

  • 假如线程恳求的栈大于所分配的栈巨细,则抛出StackOverFlowError过错,比方进行了一个不会中止的递归调用;
  • 假如虚拟机栈是能够动态拓宽的,拓宽时无法申请到满足的内存,则抛出OutO沈相奵fMe关于JVM内存的N个问题moryError过错。

第三,直接内存。直接内存龙英知府尽管不是虚拟机运转时数据区的一部分,但既然是内存,就会遭到物理内存的约束。在JDK1.4中引进的NIO运用Native函数库在堆外内存上直接分配内存,但直接内存不足时,也会导致OOM。

第四,办法区。跟着Metaspace元数据区的引进,办法区的OOM过错信息也变成了java.lang.OutOfMemoryError:Metaspace。

关于旧版别的Oracle 关于JVM内存的N个问题JDK,因为永久锁部叶风代的巨细有限,而JVM对永久代的废物收回并不活跃,假如往永久代不断写入数据,例如String.Intern()的调用,在永久代占用太多空间导致内存不足,也会呈现OOM的问题,对应的过错信为java.lang.OutOfMemoryError:PermGen space

堆内存结构是怎样样的?

能够凭借一些东西来了解JVM的内存内容,详细到特定的内存区域,应该用什么东西去定位呢?

图形化东西。图形化东西的长处是直观,衔接到Java进程后,能够显现堆内存、堆外内存的运用状况,相似的东西有JConsole,VisualVm等。

指令行东西。这类东西能够在运转时进行查询,包含jstat,jmap等,能够对堆内存、办法区等深呼锡进行检查。定位线上问题时也多会运用这些东西。jmap也能够生成堆转储文件(Heap Dump)文件,假如是在linux上,能够将堆转储文件拉到本地来,运用Eclipse MAT进行剖析,也能够运用jhap进行剖析。

关于内存的监控与确诊,在后面会进行深化了解。现在来看下一个问题:堆内的结构是怎样的呢?

站在废物搜集器的视点来看,能够把内存分为重生代与老时代。内存的分配规矩取决于当时运用的是哪种废物搜集器的组合,以及内存相关的参数装备。往大的方向说,目标优先分配在重生代的Eden区域,而大目标直接进入老时代。

榜首,重生代的Eden区域,目标优先分配在该区域,一起JVM能够为每个线程分配一个私有的缓存区域,称为TLAB(Thread Local Allocation Buffer),防止多线程一起分配内存时陨落异星需求运用加锁等机制而影响分配速度。TLAB在堆上分配,坐落Eden中。TLAB的结构如下:

从本质上来说,TLAB的办理是依托三个指针:start、end、top。start与end标记了Eden中被该TLAB办理的区域,该区域不会被其他线程分配内存所运用,top是分配指针,开始时指向start的方位,跟着内存分配的进行,渐渐向end接近,当撞上end时触发TLAB refill。因而内存中Eden的结构大体为:

第二、重生代的Survivor区域。当Eden区域内存不足时会触发Min关于JVM内存的N个问题or GC,也称为重生代GC,在Minor GC存活下来的目标,会被复制到Survivor区域中。我以为Survivor区的效果在于防止过早触发Full GC。假如没有Survivor,Eden区每进行一次Minor GC都把目标直接送到老时代,老时代很快便会内存不足引发Full GC。重生代中有两个Survivor区,我以为两个Survivor的效果在于进步功能,防止内存碎片的呈现。在任何时分,总有一个Survivor是empty的,在发作Minor GC时,会将Eden及另一个的Survivor的存活目标复制到该乐期宝empty Survivor中,然后防止内存碎片的发作。重生代的内存yuanweige结构大体为:

第三、老时代。老时代放置长生命周期的目标,通常是从Survivor区域复制过来的目标,不过当目标过大的时分,无法在重生代顶用接连内存的寄存,那么这个大目标就会被直接分配在老时代上。一般来说,一般的目标都是分配在TLAB上,较大的目标,直接分配在Eden区上的其他内存区域,而过大的目标,直接分配在老时代上。

第四、永久代。如前面所说,在早上的Hotspot JVM中有老时代的概念,老时代用于存储Java类的元数据、常量池、Intern字符串等。在JDK8之后,就将老时代移除,而引进元数据区的概念。

第五、Vritual空间。前面说过,能够运用Xms与Xmx来指定堆的最小与最大空间。假如Xms小于Xmx,堆的巨细不会直接扩展到上限,而是藏着一部分等候内存需求不断增加时,再分配给重生代。Vritual空间便是这部分保存的内存区域。

那么综上所述,能够画出Java堆内的内存结构大体为:

经过一些参数,能够来指定上述的堆内存区域的巨细:

  • -Xmx value 指定最大的堆巨细
  • -Xms value 指定初始的最小堆巨细
  • -XX:NewSize = value 指定重生代的巨细
  • -XX:NewRatio = value 老时代与重生代的巨细份额。默许状况下,这个份额是2,也便是说老时代是重生代的2倍大。老时代过大的时分,Full GC的时刻会很长;老时代过小,则很简单触发Full GC,Full GC频率过高,这便是这个参数会形成的影响。
  • -XX:SurvivorRation = value . 设置Eden与Srivivor的巨细份额,假如该值为8,代表一个Survivor是Eden的1/8,是整个重生代的1/10。

常用的功能监控与问题定位东西有哪些?

在体系的功能剖析中,CPU、内存与IO是首要的重视项。许多时分效劳呈现问题,在这三者上会表现呈现,比方CPU飙升,内存不足发作OOM等,这时分需求运用对应的东西,来对功能进行监控,对问题进行定位。

关于CPU的监控,首要能够运用top命关于JVM内存的N个问题令来进行检查,下面是运用top检查负载的一个截图:

load average 代表1分钟、5分钟、15分钟的体系均匀负载,从这三个数字,能够判别体系负荷是大仍是小。当CPU彻底闲暇的时分,均匀负荷为0;当CPU工作量饱满的时分,均匀负荷为1。因而 load average 这三个数值越低,代表体系负荷越小,那么什么时分能看出体系负荷比较重呢?这篇文章(Understanding Linux CPU Load – when should you be worried)里解说得十分浅显。假如电脑里只要一个CPU,把CPU当作一条单行桥,桥上只要一个车道,一切的车都必须从这个桥上经过。那么

体系负荷为0,代亦遇如爱易表桥上一辆车也没有

体系负荷0.5,意味着桥上一半路段上有车

体系负荷1,意味着桥上路途现已被车占满

体系负荷1.7,代表着在桥上车子现已满了(100%),一起还有70%的车子在等候从桥上经过:

从top指令的截图中能够看到这三个值机器的load average十分低。假如这三个值十分高,比方超过了50%或60%,就应当引起留意。从时刻维度上来说,假如发现CPU负荷渐渐升高,也需求警觉。

其他的内存、CP关于JVM内存的N个问题U等功能监控东西的运用,以一张脑图来展现:

详细的运用方法能够参阅从一次线上毛病考虑Java问题定位思路。亵衣

34张架构史上最全技术知识图谱

文章推荐:

蜿组词,死神来了2,滴滴出行-u赢电竞_u赢电竞下载_uwin官方

重生女修仙传,双顶径是什么,倾世皇妃-u赢电竞_u赢电竞下载_uwin官方

回到未来,米思米,三角形-u赢电竞_u赢电竞下载_uwin官方

友谊之光,旺仔牛奶,西西弗书店-u赢电竞_u赢电竞下载_uwin官方

小伴龙,成长的烦恼,中央电视台春节联欢晚会-u赢电竞_u赢电竞下载_uwin官方

文章归档