一、JVM运行时内存布局

java 8虚拟机规范的原始表达:(jvm)Run-Time Data Areas, 暂时翻译为"jvm运行时内存布局"。

从概念上大致分为6个(逻辑)区域,参考下图(注:Method Area中还有一个常量池区,图中未明确标出)

这6块区域按是否被线程共享,可以分为二大类:

一类是每个线程所独享的:

1. PC Register:也称为程序计数器, 记录每个线程当前执行的指令信息(eg:当前执行到哪一条指令,下一条该取哪条指令)

2. JVM Stack: 也称为虚拟机栈,记录每个栈帧(Frame)中的局部变量、方法返回地址等。注:这里出现了一个新名词“栈帧”,它的结构如下:

线程中每次有方法调用时,会创建Frame,方法调用结束时Frame销毁。

3. Native Method Stack: 本地(原生)方法栈,顾名思义就是调用操作系统原生本地方法时,所需要的内存区域。

上述3类区域,生命周期与Thread相同,即:线程创建时,相应的内存区创建,线程销毁时,释放相应内存。

另一类是所有线程共享的:

1. Heap:即鼎鼎大名的堆内存区,也是GC垃圾回收的主站场,用于存放类的实例对象及Arrays实例等。

2. Method Area:方法区,主要存放类结构、类成员定义,static静态成员等。

3. Runtime Constant Pool:运行时常量池,比如:字符串,int -128~127范围的值等,它是Method Area中的一部分。

Heap、Method Area 都是在虚拟机启动时创建,虚拟机退出时释放。

注:Method Area 区,虚拟机规范只是说必须要有,但是具体怎么实现,是交给具体的JVM实现去决定的,逻辑上讲,视为Heap区的一部分。所以,如果你看见类似下面的图,也不要觉得画错了。

上述6个区域,除了PC Register区不会抛出StackOverflowError或OutOfMemoryError ,其它5个区域,当请求分配的内存不足时,均会抛出OutOfMemoryError (即:OOM),其中thread独立的JVM Stack区及Native Method Stack区还会抛出StackOverflowError.

最后,还有一类不受JVM虚拟机管控的内存区,这里也提一下,即:堆外内存

可以通过Unsafe和NIO包下的DirectByteBuffer来操作堆外内存。如上图,虽然堆外内存不受JVM管控,但是堆内存中会持有对它的引用,以便进行GC。

提一个问题:总体来看,JVM把内存划分为“栈(stack)”与“堆(heap)”二大类,为何要这样设计?

个人理解:程序运行时,内存中的信息大致分为二类,一是跟程序执行逻辑相关的指令数据(这类数据通常不大,而且生命周期短),一是跟对象实例相关的数据(这类数据可能会很大,而且可以被多个线程长时间内反复共用,比如字符串常量、缓存对象这类),将这二类特点不同的数据分开管理,体现了软件设计上“模块隔离”的思想(好比,我们通常会把后端service与前端website解耦类似),也更便于内存管理。

二、GC垃圾回收原理

2.1 如何判断对象是垃圾 ?

有二种经典的判断方法,借用网友的图(文中最后有给出链接):

引用计数法,思路很简单,但是如果出现循环引用,即:A引用B,B又引用A,这种情况下就不好办了,所以JVM中使用了另一种称为“可达性分析”的判断方法:

还是刚才的循环引用问题(也是某些公司面试官可能会问到的问题),如果A引用B,B又引用A,这2个对象是否能被GC回收? 答案:关键不是在于A,B之间是否有引用,而是A,B是否可以一直向上追溯到GC Roots。如果与GC Roots没有关联,则会被回收,否则将继续存活。

上图是一个用“可达性分析”标记垃圾对象的示例图,灰色的对象表示不可达对象,将等待回收。

2.2 哪些内存区域需要GC ?

在第一部分JVM内存布局中,我们知道了thread独享的区域:PC Regiester、JVM Stack、Native Method Stack,其生命周期都与线程相同(即:与线程共生死),所以无需GC。线程共享的Heap区、Method Area则是GC关注的重点对象。

2.3 常用的GC算法:

a. mark-sweep 标记清除法

如上图,黑色区域表示待清理的垃圾对象,标记出来后直接清空。该方法很简单快速,但是缺点也很明显,会产生很多内存碎片。

b. mark-copy 标记复制法

思路也很简单,将内存对半分,总是保留一块空着(上图中的右侧),将左侧存活的对象(浅灰色区域)复制到右侧,然后左侧全部清空。避免了内存碎片问题,但是内存浪费很严重,相当于只能使用50%的内存。

c. mark-compact 标记-整理(也称标记-压缩)法

避免了上述二种算法的缺点,将垃圾对象清理掉后,同时将剩下的存活对象进行整理挪动(类似于windows的磁盘碎片整理),保证它们占用的空间连续,这样就避免了内存碎片问题,但是整理过程也会降低GC的效率。

d. generation-collect 分代收集算法

上述三种算法,每种都有各自的优缺点,都不完美。在现代JVM中,往往是综合使用的,经过大量实际分析,发现内存中的对象,大致可以分为二类:有些生命周期很短,比如一些局部变量/临时对象,而另一些则会存活很久(典型的,比如websocket长连接中的connection对象),如下图:

纵向y轴可以理解分配内存的字节数,横向x轴理解为随着时间流逝(伴随着GC),可以发现大部分对象其实相当短命,很少有对象能在GC后活下来。因此诞生了分代的思想,以Hotspot为例(JDK 7):

将内存分成了三大块: 年青代(Young Genaration),老年代(Old Generation),永久代(Permanent Generation),其中Young Genaration更是又细为分eden,S0, S1三个区。

结合我们经常使用的一些jvm调优参数后,一些参数能影响的各区域内存大小值,示意图如下:

注:jdk8开始,用MetaSpace区取代了Perm区(永久代),所以相应的jvm参数变成-XX:MetaspaceSize 及 -XX:MaxMetaspaceSize

以Hotspot为例,我们来分析下GC的主要过程:

刚开始时,对象分配在eden区,s0(即:from)及s1(即:to)区,几乎是空着

随着应用的运行,越来越多的对象被分配到eden区

当eden区放不下时,就会发生minor GC(也被称为young GC),第1步当然是要先标识出不可达垃圾对象(即:下图中的黄色块),然后将可达对象,移动到s0区(即:4个淡蓝色的方块挪到s0区),然后将黄色的垃圾块清理掉,这一轮过后,eden区就成空的了。--注:这里其实已经综合运用了“【标记-清理eden】 + 【标记-复制 eden->s0】”算法。

随着时间推移,eden如果又满了,再次触发minor GC,同样还是先做标记,这时eden和s0区可能都有垃圾对象了(下图中的黄色块),注意:这时s1(即:to)区是空的,s0区和eden区的存活对象,将直接搬到s1区。然后将eden和s0区的垃圾清理掉,这一轮minor GC后,eden和s0区就变成了空的了。

继续,随着对象的不断分配,eden空可能又满了,这时会重复刚才的minor GC过程,不过要注意的是,这时候s0是空的,所以s0与s1的角色其实会互换,即:存活的对象,会从eden和s1区,向s0区移动。然后再把eden和s1区中的垃圾清除,这一轮完成后,eden与s1区变成空的。(如下图)

对于那些比较“长寿”的对象一直在s0与s1中挪来挪去,一来很占地方,而且也会造成一定开销,降低gc效率,于是有了“代龄(age)”及“晋升”,对象在年青代的3个区(edge,s0,s1)之间,每次从1个区移到另1区,年龄+1,在young区达到一定的年龄阈值后,将晋升到老年代(下图中是8,即:挪动8次后,如果还活着,下次minor GC时,将移动到Tenured区)

下图是晋升的主要过程:对象先分配在年青代,经过多次Young GC后,如果对象还活着,晋升到老年代。

如果老年代,最终也放满了,就会发生major GC(即Full GC),由于老年代的的对象通常会比较多,因为标记-清理-整理(压缩)的耗时通常会比较长,会让应用出现卡顿的现象,这也是为什么很多应用要优化,尽量避免或减少Full GC的原因。

注:上面的过程主要来自oracle官网的资料,但是有一个细节官网没有提到,如果分配的新对象比较大,eden区放不下,但是old区可以放下时,会直接分配到old区(即没有晋升这一过程,直接到老年代了)。

下图引自阿里出品的<<码出高效-Java开发手册>>一书,梳理了GC的主要过程。

三、垃圾回收器

不算最新出现的神器ZGC,历史上出现过7种经典的垃圾回收器。

这些回收器都是基于分代的,把G1除外,按回收的分代划分,横线以上的3种:Serial ,ParNew, Parellel Scavenge都是回收年青代的,横线以下的3种:CMS,Serial Old, Parallel Old 都是回收老年代的

3.1 Serial 收集器

单线程用标记-复制算法,快刀斩乱麻,单线程的好处避免上下文切换,早期的机器,大多是单核,也比较实用。但执行期间,会发生STW(Stop The World)

3.2 ParNew 收集器

Serial的多线程版本,同样会STW,在多核机器上会更适用。

3.3 Parallel Scavenge 收集器

ParNew的升级版本,主要区别在于提供了二个参数:-XX:MaxGCPauseMillis 最大垃圾回收停顿时间; -XX:GCTimeRatio 垃圾回收时间与总时间占比,通过这2个参数,可以适合控制回收的节奏,更关注于吞吐率(即:总时间与垃圾回收时间的比例)。

3.4 Serial Old 收集器

因为老年代的对象通常比较多,占用的空间通常也会更大,如果采用复制算法,得留50%的空间用于复制,相当不划算,而且因为对象多,从1个区,复制到另1个区,耗时也会比较长,所以老年代的收集,通常会采用“标记-整理”法。从名字就可以看出来,这是单线程(串行)的, 依然会有STW

3.5 Parallel Old 收集器

一句话:Serial Old的多线程版本

3.6 CMS 收集器

全称:Concurrent Mark Sweep,从名字上看,就能猜出它是并发多线程的。这是JDK 7中广泛使用的收集器,有必要多说一下,借一张网友的图说话:

相对3.4 Serial Old收集器或3.5 Parallel Old收集器而言,这个明显要复杂多了,分为4个阶段:

1、 Inital Mark 初始标记: 主要是标记GC Root开始的下级(注:仅下一级)对象,这个过程会STW,但是跟GC Root直接关联的下级对象不会很多,因为这个过程其实很快。

2、 Concurrent Mark 并发标记:根据上一步的结果,继续向下标识所有关联的对象,直到这条链上的最尽头。这个过程是多线程的,虽然耗时理论上会比较长,但是其它工作线程并不会阻塞,没有STW。

3、 Remark 再标志:为啥还要再标记一次?因为第2步并没有阻塞其它工作线程,其它线程在标识过程中,很有可能会产生新的垃圾。试想下,高铁上的垃圾清理员,从车厢一头开始吆喝“有需要扔垃圾的乘客,请把垃圾扔一下”,一边工作一边向前走,等走到车厢另一头时,刚才走过的位置上,可能又有乘客产生了新的空瓶垃圾。所以,要完全把这个车厢清理干净的话,她应该喊一下:所有乘客不要再扔垃圾了(STW),然后把新产生的垃圾收走。当然,因为刚才已经把收过一遍垃圾,所以这次收集新产生的垃圾,用不了多长时间(即:STW时间不会很长)

4、 Concurrent Sweep:并行清理,这里使用多线程以“Mark Sweep-标记清理”算法,把垃圾清掉,其它工作线程仍然能继续支行,不会造成卡顿。等等,刚才我们不是提到过“标记清理”法,会留下很多内存碎片吗?确实,但是也没办法,如果换成“Mark Compact标记-整理”法,把垃圾清理后,剩下的对象也顺便排整理,会导致这些对象的内存地址发生变化,别忘了,此时其它线程还在工作,如果引用的对象地址变了,就天下大乱了。另外,由于这一步是并行处理,并不阻塞其它线程,所以还有一个副使用,在清理的过程中,仍然可能会有新垃圾对象产生,只能等到下一轮GC,才会被清理掉。

虽然仍不完美,但是从这4步的处理过程来看,以往收集器中最让人诟病的长时间STW,通过上述设计,被分解成二次短暂的STW,所以从总体效果上看,应用在GC期间卡顿的情况会大大改善,这也是CMS一度十分流行的重要原因。

3.7 G1 收集器

G1的全称是Garbage-First,为什么叫这个名字,呆会儿会详细说明。鉴于CMS的一些不足之外,比如: 老年代内存碎片化,STW时间虽然已经改善了很多,但是仍然有提升空间。G1就横空出世了,它对于heap区的内存划思路很新颖,有点算法中分治法“分而治之”的味道。

如下图,G1将heap内存区,划分为一个个大小相等(1-32M, 2的n次方)、内存连续的Region区域,每个region都对应Eden、Survivor 、Old、Humongous四种角色之一(注:Humongous,简称H区是专用于存放超大对象的区域,通常>= 1/2 Region Size,且只有Full GC阶段,才会回收H区,避免了频繁扫描、复制/移动大对象),但是region与region之间不要求连续。所有的垃圾回收,都是基于1个个region的。JVM内部知道,哪些region的对象最少(即:该区域最空),总是会优先收集这些region(因为对象少,内存相对较空,肯定快),这也是Garbage-First得名的由来,G即是Garbage的缩写, 1即First(第1)。

G1 Young GC

young GC前:

young GC后:

理论上讲,只要有一个Empty Region(空区域),就可以进行垃圾回收。

由于region与region之间并不要求连续,而使用G1的场景通常是大内存(比如:64G甚至更大),为了提高扫描根对象和标记的效率,G1使用了二个新的辅助存储结构:

Remembered Sets:简称RSets,用于根据每个region里的对象,是从哪指向过来的(即:谁引用了我),每个Region都有独立的RSets。(Other Region -> Self Region)

Collection Sets :简称CSets,记录了等待回收的Region集合,GC时这些Region中的对象会被回收(copied or moved)。

RSets的引入,在YGC时,将年青代Region的RSets做为根对象,可以避免扫描老年代的region,能大大减轻GC的负担(注:在老年代收集Mixed GC时,RSets记录了Old->Old的引用,也可以避免扫描所有Old区)

Old Generation Collection(也称为 Mixed GC)

(按oracle官网文档描述分为)5个阶段: Initial Mark(STW) -> Root Region Scan -> Cocurrent Marking -> Remark(STW) -> Copying/Cleanup(STW && Concurrent)

(注:也有很多文章会把Root Region Scan省略掉,合并到Initial Mark里,变成4个阶段)

(上图)存活对象的"初始标记"依赖于Young GC,GC 日志中会记录成young字样。

(上图),并发标记过程中,如果发现某些region全是空的,会被直接清除。

(上图)进入重新标记阶段。

(上图)并发复制/清查阶段。这个阶段,Young区和Old区的对象有可能会被同时清理。GC日志中,会记录为mixed字段,这也是G1的老年代收集,也称称为Mixed GC的原因。

上图是,老年代收集完后的示意图。

通过这几个阶段的分析,虽然看上去很多阶段仍然会发生STW,但是G1提供了一个预测模型,通过统计方法,根据历史数据来预测本次收集,需要选择多少个Region来回收,尽量满足用户的预期停顿值(-XX:MaxGCPauseMillis参数可指定预期停顿值)

注:如果Mixed GC仍然效果不理想,跟不上新对象分配内存的需求,会使用Serial Old GC(Full GC)强制收集整个Heap.

小结:与CMS相比,G1有内存整理过程(标记-压缩),避免了内存碎片;STW时间可控(能预测GC停顿时间)

3.8 ZGC (截止目前为止,史上最好的GC收集器)

在G1的基础上,做了很多改进(JDK 11开始引入)

3.8.1 动态调整大小的Region

G1中每个Region的大小是固定的,但ZGC创建和销毁Region,可以动态调整大小,内存使用更高效。

3.8.2 不分代,干掉了RSets

G1中每个Region需要借助额外的RSets来记录“谁引用了我”,占用了额外的内存空间,每次对象移动时,RSets也需要更新,会产生开销。

注:ZGC没有为止,没有实现分代机制,每次都是并发的对所有region进行回收,不象G1是增量回收,所以用不着RSets( 不分代的带来的可能性能下降,会用下面马上提到的Colored Pointer && Load Barrier来优化)

3.8.3 带颜色的指针 Colored Pointer

这里的指针类似java中的引用,意为对某块虚拟内存的引用。ZGC采用了64位指针(注:目前只支持linux 64位系统),将42-45这4个bit位置赋予了不同的含义(即:所谓的颜色标志位,也换为指针的metadata)

finalizable位: 仅finalizer(类比c++中的析构函数)可访问;
remap位:指向对象当前(最新)的内存地址 (参考下面提到的relocation);
marked0 && marked1 位: 用于标志可达对象;

这4个标志位,同一时刻只会有1个位置是1。每当指针对应的内存数据发生变化(比如:内存被移动),颜色会发生变化。

3.8.4 读屏障 Load Barrier

传统GC做标记时,为了防止其它线程在标记期间修改对象,通常会简单的STW。而ZGC有了Colored Pointer后,引入了所谓的读屏障,当指针引用的内存正被移动时,指针上的颜色就会变化,ZGC会先把指针更新成最新状态,然后再返回。(大家可以回想下java中的volatile关键字,有异曲同工之妙),这样仅读取该指针时可能会略有开销,而不用将整个heap STW。

3.8.5 重定位 relocation

如上图,在标记过程中,先从Roots对象找到了直接关联的下级对象1,2,4

然后继续向下层标记,找到了5,8对象, 此时已经可以判定 3,6,7为垃圾对象。

如果按常规思路,一般会将8从最右侧的Region移动(或复制到)中间的Region,然后再将中间Region的3干掉,最后再对中间Region做压缩compact整理。但ZGC做得更高明,它直接将4,5复制到了一个空的新Region就完事了,然后中间的2个Region直接废弃(或理解为“释放”,做为下次回收的“新”Region), 这样的好处是避免了中间Region的compact整理过程。

最后,指针重新调整为正确的指向(即:remap),而且上一阶段的remap与下一阶段的mark是混在一起处理的,相对更高效。

Remap的流程图如下:

3.8.6 多重映射 Multi-Mapping

这个优化,说实话没完全看懂,只能谈下自己的理解(如果有误,欢迎指正):虚拟内存与实际物理内存,OS会维护一个映射关系,才能正常使用。如下图:

zgc的64位颜色指针,在解除映射关系时,代价较高(需要屏蔽额外的42-45的颜色标志位)。考虑到这4个标志位,同1时刻,只会有1位置成1(如下图),另外finalizable标志位,永远不希望被解除映射绑定(可不用考虑映射问题)。 所以剩下3种颜色的虚拟内存,可以都映射到同1段物理内存(即:映射复用,或者更通俗点讲,本来3种不同颜色的指针,哪怕0-41位完全相同,也需要映射到3段不同的物理内存,现在只需要映射到同1段物理内存即可)。

3.8.7 支持NUMA架构

NUMA是一种多核服务器的架构,简单来讲,一个多核服务器(比如:2core),每个cpu都有属于自己的存储器,会比访问另一个核的存储器会慢很多(类似于就近访问更快)。相对之前的GC算法,ZGC首次支持了NUMA架构,申请堆内存时,判断当前线程属是哪个CPU在执行,然后就近申请该CPU能使用的内存。

小结:革命性的ZGC经过上述一堆优化后,每次GC总体卡顿时间按官方说法<10ms。(注:启用zgc,需要设置-XX:+UnlockExperimentalVMOptions -XX:+UseZGC)

最后附上一段OOM的常用测试代码:

  

命令行测试方法:

一、 openjdk 11.0.3 环境: + G1回收

a、验证heap OOM

把main方法中的test.heapOOM()行,注释打开,然后命令行下运行:

1
java -Xmx10M -XX:+UseG1GC -Xlog:gc* -Xlog:gc:gc.log -XX:+HeapDumpBeforeFullGC  OOMTest.java

最后会输出:

其中 OutOfMemoryError:Java heap space即表示heap OOM

b、验证stack溢出

把main方法中的test.stackOverflow()行,注释打开,然后命令行下运行:

1
java -Xmx20M -Xss180k -XX:+UseG1GC -Xlog:gc* -Xlog:gc:gc.log  -XX:+HeapDumpBeforeFullGC OOMTest.java

最后会输出:

其中 OutOfMemoryError: Metaspace 即表示Metaspace区OOM

d、验证堆外内存OOM

把main方法中的test.directOOM()行,注释打开,然后命令行下运行:

最后会输出:

其中OutOfMemoryError行并没有输出具体哪个区(注:堆外内存不属于JVM内存中的任何一个区,所以无法输出),但紧接着有一行jdk.internal.misc.Unsafe.allocateMemory 可以看出是"堆外内存直接分配"导致的异常

二、openjdk 1.8.0_212 + CMS回收

jdk1.8下,java命令无法直接运行.java文件,必须先编译,即:

java OOMTest.java 成功后,会生成OOMTest.class文件, 然后再可以参考下面的命令进行测试:

a、heap OOM测试

1
java -Xmx10M -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -XX:+HeapDumpOnOutOfMemoryError OOMTest

b、验证stack溢出

1
java -Xmx10M -Xss128k -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -XX:+HeapDumpOnOutOfMemoryError OOMTest

c、验证metaspace OOM

1
java -Xmx20M -XX:+UseConcMarkSweepGC -XX:MaxMetaspaceSize=10M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -XX:+HeapDumpOnOutOfMemoryError OOMTest

d、验证堆外内存OOM

1
java -Xmx20M -XX:+UseConcMarkSweepGC -XX:MaxDirectMemorySize=10M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -XX:+HeapDumpOnOutOfMemoryError OOMTest

生成的gc日志文件,可以用开源工具GCViewer查看,这是一个纯java写的GUI程序,使用很简单,File→Open File 选择gc日志文件即可(目前支持CMS/G1生成的日志文件,另外如果GC文件过大时,可能打不开)

GCViewer可以很方便的统计出GC的类型,次数,停顿时间,年青代/老年代的大小等,还有图表显示,非常方便。

参考文章:

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5

https://blog.csdn.net/heart_mine/article/details/79495032

https://www.programcreek.com/2013/04/jvm-run-time-data-areas/

https://javapapers.com/core-java/java-jvm-run-time-data-areas/

https://javapapers.com/core-java/java-jvm-memory-types/

https://cloud.tencent.com/developer/article/1152616

https://www.jianshu.com/p/17e72bb01bf1

http://calvin1978.blogcn.com/articles/directbytebuffer.html

https://www.cnkirito.moe/nio-buffer-recycle/

https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

http://inbravo.github.io/html/jvm.html

https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/G1GettingStarted/index.html

https://segmentfault.com/a/1190000009783873

https://segmentfault.com/a/1190000016551339

https://www.team-bob.org/things-about-java-garbage-collection-1/2/

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html

https://tech.meituan.com/2016/09/23/g1.html

https://mp.weixin.qq.com/s/KUCs_BJUNfMMCO1T3_WAjw

https://www.baeldung.com/jvm-zgc-garbage-collector

http://xxfox.perfma.com/jvm/

https://wiki.openjdk.java.net/display/zgc/Main

http://cr.openjdk.java.net/~pliden/slides/ZGC-FOSDEM-2018.pdf

http://www.ishenping.com/ArtInfo/43701.html

http://likehui.top/2019/04/11/ZGC-%E7%89%B9%E6%80%A7%E8%A7%A3%E8%AF%BB/

作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com

 

JVM内存布局及GC知识的更多相关文章

  1. JVM内存布局及GC知识回顾

    注:本文篇幅较长,且需要有一定的java基础,建议各位看官,备好瓜子.饮料.小板凳,摆个让自己舒服的姿势,慢慢细看^_^, 文中所有素材,均来自互联网,本人只是详细梳理了一遍,形成此文. 一.JVM运 ...

  2. [转帖]详解JVM内存布局及GC原理,值得收藏

    概述 https://www.toutiao.com/i6731345429574713868/ java发展历史上出现过很多垃圾回收器,各有各的适应场景,不仅仅是开发,作为运维也需要对这方面有一定的 ...

  3. 管中窥豹——从对象的生命周期梳理JVM内存结构、GC调优、类加载、AOP编程及性能监控

    如题,本文的宗旨既是透过对象的生命周期,来梳理JVM内存结构及GC相关知识,并辅以AOP及双亲委派机制原理,学习不仅仅是海绵式的吸收学习,还需要自己去分析why,加深对技术的理解和认知,祝大家早日走上 ...

  4. JVM(二)JVM内存布局

    这几天我再次阅读了<深入理解Java虚拟机>之第二章"Java内存区域与内存溢出异常",同时也参考了一些网上的资料,现在把自己的一些认识和体会记录一下.  (本文为博主 ...

  5. JVM内存模型及GC回收算法

    该篇博客主要对JVM内存模型以及GC回收算法以自己的理解和认识做以记录. 内存模型 GC垃圾回收 1.内存模型 从上图可以看出,JVM分为 方法区,虚拟机栈,本地方法栈,堆,计数器 5个区域.其中最为 ...

  6. 深入理解Java虚拟机之JVM内存布局篇

    内存布局**** ​ JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JVM的稳定高效运行.不同的JVM对于内存的划分方式和管理机制存在部分差异.结合JVM虚拟机规范,一起来 ...

  7. JVM 内存布局

    JVM 内存布局规定了 Java 在运行过程中内存申请.分配.管理的策略,保证了 JVM 的高效稳定运行. 线程是否共享 Heap (堆区) 堆是 OOM 故障最主要的发生区域.它是内存区域中最大的一 ...

  8. JVM内存管理之GC算法精解(复制算法与标记/整理算法)

    本次LZ和各位分享GC最后两种算法,复制算法以及标记/整理算法.上一章在讲解标记/清除算法时已经提到过,这两种算法都是在此基础上演化而来的,究竟这两种算法优化了之前标记/清除算法的哪些问题呢? 复制算 ...

  9. Jvm 内存浅析 及 GC个人学习总结

    从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C ...

随机推荐

  1. codemirror 行高 字体 行间距 设置

    用的是 react-codemirror2 样式文件: ... .code-mirror{ font-size : 13px; line-height : 150%; } ... 引入样式文件: .. ...

  2. java.util.Date和jdk1.8新时间API比拼

    旧的时间和日期的API的缺陷 Java 的 java.util.Date 和 java.util.Calendar 类易用性差,不支持时区,而且都不是线程安全的. Date如果不格式化,打印出的日期可 ...

  3. Linux 内核 启动时间

    为见到 PCI 如何工作的, 我们从系统启动开始, 因为那是设备被配置的时候. 当一个 PCI 设备上电时, 硬件保持非激活. 换句话说, 设备只响应配置交易. 在上电时, 设备没有内存并且没有 I/ ...

  4. location对象相关

    JS是由DOM(文档对象模型).BOM(浏览器对象模型).以及ECMA组成,而location对象是BOM中的一个非常重要的对象,所有关于地址栏信息的内容都在这里.了解location对象之前让我们先 ...

  5. mybatis 整合redis作为二级缓存

    核心关键在于定义一个RedisCache实现mytis实现的Cache接口 ** * @author tele * @Description RedisCache由于需要传入id, 由mybatis进 ...

  6. Team Foundation Server 2015使用教程【1】:团队项目创建

  7. Microsoft Edge 离线安装包下载

    Microsoft Edge 现已准备就绪 下一版 Microsoft Edge 已准备好进行企业评估. 立即下载离线安装程序.查看管理策略并尝试 Internet Explorer 模式. http ...

  8. IPv4数据报格式及其语义

    一.IP数据报的格式如下图所示 版本 首部长度 服务类型 数据报长度 16比特标识 标志 13比特片偏移 寿命 上层协议 首部检验和 32比特源IP地址 32比特目的IP地址 选项(如果有的话) 数据 ...

  9. mysql 时间函数总结

    1. 获取当前时间   select now(); // 2018-08-24 11:01:26   select unix_timestamp(); // 1535079695   总结:unix_ ...

  10. 从0开发3D引擎(一):开篇

    介绍 大家好,本系列带你踏上Web 3D编程之旅- 本系列是实战类型,从0开始带领读者写出"良好架构.良好扩展性.最小功能集合(MVP)" 的3D引擎. 本系列的素材来自我们的产品 ...