-
jvm中的内存机制
-
jvm的内存空间分为两大区域,一个堆一个栈,堆里面放着new出来的东西,还有数组对象也会在堆里,栈中放的是对象的引用,栈里面的引用去指向堆空间new出来的地址的首地址,这样的话就可以找到它的位置,进行数据存储。
-
jvm开内存是在堆中去开,因为它的堆是非常大的,堆里面有这么几个区域,青年带,老年带。青年带里面有伊甸园区,开辟的所有对象都会在伊甸园区,当伊甸园区的内存不够时,它会自己去到堆空间中的老年带里去加,这时候,如果判断创建的对象要比老年带里的还大,则抛出异常。这个是jvm的内存溢出,它和数据库的内存溢出不太一样,数据库的话是会把超量的数据直接往内存里去塞,知道内存满了,抛出异常,这条数据库的连接作废,因为数据库其他操作也走这条操作路线,一个卡死,其他都会卡死。
-
然后在堆中还会有一个空间,它存储的是类信息,静态资源和常量,这里面的数据我们放到元空间,元空间是堆中的一块区域,但他却叫做非堆区,元空间里面的数据和对空间里面的数据在线程概念中是一样的,叫做线程共享,所以静态资源,类信息和常量还有new出来的对象,数组都会有线程安全问题。
-
数据类型没有线程安全问题,栈里面数据是线程私有的。
-
程序计数器
-
线程私有,没有线程安全问题,因为是多线程的,比如现在在主线程里去运行,我开一条线程,现在这条线程被别的任务占用了,那么程序计数器会在这条线程开始运行的地方打一个标记,线程运行完了回来了之后,主线程会接着计数。
-
-
本地方法栈
-
本地方法栈中没有所谓的方法,本地方法栈里面是我们java底层核心中的HotSpot(虚拟机)里面的方法,比如我们java是没有开发内存操作的,但是我们Native里面的方法是直接操作内存的。就是底层的操作内存的方法是在本地方法栈里面。一般Native底层是用c编写的,交互底层硬件。
-
-
方法栈
-
方法栈进行弹栈和压栈,调用了一个方法就会进行压栈,方法执行完了会进行弹栈。
-
-
内存回收机制
-
一个对象在伊甸园区没有被回收的话,S0,S1在回收的时候进行交换操作,他会从S0进到S1区如果回收一次之后,对象还在,则S1到S0。越不被清除的对象越不好回收,假如它每次都避过去了。第一种方法是手动置null。第二种就是对象进入老年带,所有没有被回收的对象都要进入老年带。老年带要做的事情就是要把没有回收的对象全部进行左移,左移到老年带区域。它每次去进行回收的时候都会对数据进行标记,标记15次,在第16次回收的时候,一定会回收掉。
-
为什么不在老年带直接就把对象回收了呢
-
因为回收这件事是很耗内存的,每次回收都会造成java挂起状态,经常回收,程序运行会变慢,所以回收的话是按照垃圾回收器里的规则进行回收。
-
-
垃圾回收器
-
按照线程分:串行和并行
-
按照功能分:并发和独占
-
按照碎片处理方式分:压缩式和非压缩式,压缩式垃圾回收会在回收完成之后对存活对象进行压缩整理,消除回收后的碎片。非压缩式不会。
-
按照工作区间分:年轻带和老年带
-
性能指标:
-
吞吐量:运行代码时间占总运行时间的比例
-
暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间
-
内存占用:java堆区所占内存大小
-
-
设计方向,在大吞吐量优先的情况下,降低停顿时间
-
常见的垃圾回收器
-
串行回收器:Serial,SerialOld
-
并行回收器:ParNew,ParalleScavenge,ParallelOld
-
并发回收器:GMS,G1
-
-
回收器在垃圾分代分布
-
年轻带:Serial,ParalleScavenge,ParNew,G1
-
老年带:SerialOld,ParallelOld,GMS,G1
-
G1是整堆回收,单线程回收,具有Nio代理,GMS是老年带回收,多线程挂起回收
-
-
GMS并发收集器
-
标记清除算法
-
初始标记:程序短暂暂停,该阶段主要标记出Roots能直接关联到的对象。
-
并发标记:该阶段是要根据上个阶段标记的对象扫描整个对象图,耗时较长,但是可以和用户线程同时执行。
-
重新标记:该阶段为了修正并发期间,用户修改的对象的记录,耗时短于并发长于初始标记,需要程序暂停。
-
并发 清理:该阶段对标记好的对象进行清理,和用户线程同时执行。
-
-
GMS是在jdk9之前使用的一款GMS,现在市面上大都采用的都是这个。但是GMSGC会有弊端
-
做不到完全的并发,只能算低停顿
-
做不到老年带完全填满再收集,只能设置一个阈值再进行回收,如果预留的内存不足以满足程序的运行,则会报错,这时候系统会临时启动SerialOld默认的老年带GC,这样耗时就更长了。
-
在并发期间会可能有用户对标记的垃圾进行修改,所以会产生碎片。
-
-
-
GMS在jdk9中被移除,官方选择废弃。而将G1 全功能收集器放在之后的版本中。
-
G1介绍
-
分代上看,G1仍旧属于分代处理,只不过在堆的结构上,各代的分区不在坚持连续,固定大小和数量。而是分为若干区域(Region),这些区域包含了逻辑上的各个分代。
-
Garbage First,是一个并行回收器,他把堆内存分割成许多不连续的区域,使用不同的区域来表示S0(幸存者0区),S1(幸存者1区),老年带等。
-
G1有计划的避免了在整个java堆中进行全区域的垃圾收集,转以跟踪各个区域的垃圾堆积价值大小(回收后获得的空间大小和回收所需要的时间),在后台维护一个优先级列表,根据允许的收集时间,优先回收价值最大的那块区域。
-
因为该回收机制侧重于回收垃圾最大量的区间,所以也可以称为垃圾优先机制。
-
-
G1优劣势
-
该收集器在对比GMS还不具备压倒性优势,在小内存应用上GMS还是会优于G1,G1在大内存应用上发挥着优势,平衡点在6-8G之间。
-
-
G1优势
-
G1主要面向服务器,针对配备了多核cpu以及大容量内存的机器。1.7版本正式启用。该回收器同其他回收器不同,它兼顾了年轻带和老年带。被官方称为全功能的垃圾收集器,
-
并行性,G1回收期间,可以多个GC线程工作,多核的计算能力
-
并发性,工作任务和回收任务9比1执行,一般来说在回收期间不会阻塞工作任务。
-
不再坚持于内存连续,固定大小,固定数量的分代,实行堆空间被分为若干(Region)区域的逻辑分代
-
-
参数设置
-
-XX:+UseG1GC手动指定使用G1收集器执行内存回收任务。
-
-XX:G1HeapRegionsize设置每个Region的大小。值是2的幂,范围是1MB到32MB之间,目标是根据最小的Java堆大小划分出约2048个区域。默认是堆内存的1/2000。
-
-XX:MaxGCPauseMillis设置期望达到的最大GC停顿时间指标(JVM会尽力实现,但不保证达到)。默认值是200ms.
-
-XX: Paralle1GCThread设置STW工作线程数的值。最多设置为8
-
-XX:ConcGCThreads设置并发标记的线程数。将n设置为并行垃圾回收线程数(ParallelGCThreads)的1/4左右。
-
-XX: InitiatingHeap0ccupancyPercent设置触发并发GC周期的Java堆占用率阈值。超过此值,就触发GC。默认值是45。
-
-
操作步骤
-
开启G1垃圾收集器
-
设置堆的最大内存
-
设置最大停顿时间,即gc时用户所能容忍的工作任务因GC影响而停顿的时间。
-
-
回收模式
-
YoungGC
-
ConcurrentMarking
-
MixedGC
-
FullGC
-
-
使用场景
-
面向服务端,针对大内存,多处理的机器,普通大小的堆表现并暂时不如GMS
-
最主要的应用就是需要低GC延迟且具有大堆的应用程序提供解决方案。(堆在大于6G时会有好的效果)
-
使用G1要比GMS好的场景
-
超过50%的堆被活动数据占用
-
对象分配频率或年代提升频率变化很大
-
GC停顿时间长
-
HotSpot垃圾收集器里,G1是采用应用线程承担GC工作,而其他收集器是使用内置的JVM线程执行GC,当其他线程慢时,系统会调用G1来加速垃圾回收。
-
-
-
Region介绍
-
G1在使用时,它将整个堆划分为2048个大小相同且各自独立的Region块儿,每个块大小根据实际使用来定,可以手动设置,一般控制在1MB到32MB,且为2的N次幂。
-
G1保留了新生代老年代的概念,但是不在物理隔离,通过Region动态分配实现逻辑连续。
-
除了原有的伊甸园区,年轻代,老年代,G1新增了一个H区,humongous,大对象区,如果对象大小超过了1.5个Region,就把该对象存到大对象区。
-
设置H的原因:对于堆中的大对象,默认会被直接分配到老年代,但是如果它是一个短期存在的大对象,就会对垃圾收集器造成负面影响。所以G1设置了一个H区,如果一个H区装不下,那就再寻找连续的H区存储,有时候为了能找到连续的H区,G1不得不启用FullGC。G1的大多数行为都把H区当做老年代看待。
-
-
G1主要回收环节
-
年轻代的YoungGC
-
老年代的并发标记过程ConcurrentMarking
-
混合回收MixedGC
-
为了回收失败而存在的单线程独占高强度强制回收FullGC
-
-
详细步骤
-
年轻代的YoungGC(年轻代的垃圾回收只会回收伊甸园区和青年区)
-
G1停止应用程序
-
G1创建回收集,即需要被回收的内存分段集合,包括 伊甸园区和青年区
-
开启回收
-
扫描根,根指static变量指向的对象和正在执行的方法调用链条上的局部变量等。根引用连同RSet记录的外部引用作为扫描存活对象的入口
-
更新RSet,即刷新引用关系。
-
处理RSet,识别老年代对象指向的伊甸园区中的对象,这些对象被认为是存活对象。
-
复制对象,遍历对象树,伊甸园区中的对象会被复制到青年区,青年区对象年龄如果未达到阈值,则年龄加一,年龄达到阈值则复制进入老年带,如果伊甸园区的存活对象,青年区放不下,则直接晋升到老年代。
-
处理伊甸园区的软,弱,虚,引用。清空伊甸园区。
-
-
-
老年代的并发标记过程ConcurrentMarking
-
初始化标记:标记从根节点直接可达的对象,该阶段并发,触发一次 YoungGC。
-
根区域扫描:标记青年代中可直达老年代的被引用对象,在 YoungGC前完成。
-
并发标记:整个堆进行并发标记,期间如果发现标记区域的对象都是垃圾,则立即回收。同时并发标记过程中,会去计算每块区域的对象活性(区域中存活对象的比例)。
-
为防止标记出错,再进行一次基于快照算法( snapshot-at-the-beginning (SATB))的并行标记。
-
独占清理:并行计算各个区域对象的存活比例,进行排序,识别可以混合回收的区域,该阶段只是标记,并不清理。
-
并发清理:清理识别为完全空闲的区域。
-
-
混合回收: MixedGC混合回收是可以同时回收青年代和老年代的GC工具,除了回收全部青年代Region,还会回收部分老年代Region,
-
并发标记结束之后,老年代中百分百为垃圾的内存分段被回收了,部分为垃圾的内存分段被计算出来。默认这些老年代内存分段会被最多分8次回收,次数可以通过 -XX:G1MixedGccountTarget调节。
-
混合回收8分之一老年代内存分段,伊甸园区内存分段,青年代内存分段。
-
G1会优先回收垃圾多的内存分段,垃圾占比越高,越先被回收。可以通过设置阈值( -XX:G1MixedGCLiveThresholdPercent,默认为65%)来决定。
-
混合回收不一定要8次,可设置阈值( -XX:G1HeapwastePercent,默认值为10%),意思是允许整个堆内存中有10%的空间被浪费,当发现可回收的垃圾低于10%则不再进行混合回收,因为GC花费的时间多,但是回收的内存少。
-
-
FullGC,如果以上回收出现问题,不能工作,G1就会启动备用方案,全力回收内存。
-
-
-
-
总结
-
-
-
jvm的概念理解以及G1,GMS垃圾回收机制的理解
最新推荐文章于 2024-05-24 06:55:25 发布