jvm内存分配&回收策略

前两篇文章,我给大家分别介绍了jvm的内存垃圾回收机制和gc算法以及jdk内部现阶段所有的gc回收器,接下来本来以为和大家一起直接介绍在web应用中如何去做jvm的调优实战,但是想了一下还有本篇文章的内容,jvm内存分配以及回收策略等方面的问题以及常见的面试题,在此,本篇文章为大家介绍一下!

jvm内存分配&回收策略

jvm内存结构,内存模型以及jvm内存调优相比很多的java的程序员可能一味的觉得我现在懂很多的框架,像什么常见的企业级应用框架ssh,ssm等;更何况这几年比较盛行的分布式微服务框架像springBoot+spingCloud或者Dubbo+zookeeper;以及围绕着Base理论的数据最终一致性的服务器中转软件像redis,rabbitMQ,ES...等等等等这些更为的重要。在这里我表明自己的观点,其实不然,框架我觉得是一位java程序员都应当具备的应用技能,在公司庞大的业务系统开发中,你只会用框架,在项目生产中一但出现了问题,那么你必然是知其然而不知其所以然,无法快速的解决问题。因此,所有编程语言的底层才是解决问题的最根本的手段,因此我们要熟练掌握,这样在实际的开发过程中才能运筹帷幄。

jvm内存分配

谈到jvm的内存分配,在大部分的也就是说除了G1这一个垃圾回收器之外,首先大家还是要把这张图一直铭记在自己的脑子中的!


jvm内存分配图

在java的jvm内存结构图中,我们通常把内存结构统一的称为运行时数据区,当然为了更清晰的了解运行时数据区,我给大家做了清晰的划分,当中存在我们常说的堆,栈这两个重要的数据区域!
-堆
如图所示,想要了解jvm的内存分配就要熟悉堆空间,在堆中,我给大家划分出了两个大的部分:
1.那就是新生代和老年代
①.新生代主要是刚出生的对象,比如你代码中经常用的new ,以及Method.invoke()等操作,这些操作的对象都是在Eden去先分配出内存,然后等待gc的回收,那么在这个区域里面我也提交,jvm主要做的回收就是MinorGC,jvm默认的是假如一个对象在新生代的年龄到达15岁之后,将其晋升到老年代存储。意思就是这个对象要在新生代中经历15次MinorGC之后不被回收,那么将进入老年代。当然不排除你自己的设定,可以利用参数配置使大对象在Eden出生后直接进入老年代。
大对象直接进入老年代:-XX:PretenureSizeThreshold=n(n代表你要限制的对象的字节数BIT)
②老年代主要存储的都是一些老的油条对象,前几篇文章我也都谈到,在此内存区域,是不可能采用标记复制算法的,因为那样会减少一半的空间存储量,降低程序的效率。
2.新生代中又划分出了三个区域
①Eden主要接受刚新生的对象
②Survivor0
③Survivor1
这两个内存区域主要是用于gc做垃圾回收算法时用到的,也就是MinorGC发生的主要内存区域。
了解清楚新生代和老年代的主要作用之后,此时是我今年面试时,无意见面试官提到的一个面试题,原想我现在面试的岗位根本不能达到这样的深度,在此分享给大家!

面试题

这样的一个场景,假设我现在堆内存给的是100m,新生代给的是10m,那么Survivor0或者Survivor1是多少M
这样的一个问题,在此也给大家简单的用jvm参数解读一下,就是-Xms100m -Xmx100m -Xmn10m 这样的一个简单的jvm配置参数。

解读

想要给面试官解答清楚这样一个面试题的重要点就是,最少你要清楚这些配置参数代表什么意思,面试题没我我给的上述场景分析,只有配置参数,其二也就是在jvm底层针对Eden:Survivor0:Survivor1有一个默认的比例那就是8:1:1。(我们也可以用-XX:SurvivorRatio参数控制比例)有了这样的基础之后,就是简单的计算问题了,新生代10m问s0或者s1多少m,按照比例关系,很清晰的回答都是1m!
-栈
栈这个内存区域,大家可能不会太陌生,毕竟web程序中最多的就是一些逻辑业务,那么栈的作用主要就是为了存储临时变量,动态链接,以及Method return adress等等,后续会为大家画出图,供大家理解!


虚拟机栈结构图.png

在此有两个面试题需要注意:
①普通的mvc三层业务Controller->service->dao,假设这样一个用户线程这样的去三成执行业务,那么在栈中三个方法是怎样存储的!
答:栈大家也清楚理解,里面是有很多的栈帧排布的,遵循栈的数据结构得知,先进的元素最后出去,那么必定Controller层的业务方法一定是在栈底的。dao的业务一定是在栈顶的。
②是什么造成的栈溢出。java.lang.StackOverflowError
大家分析一下,栈内一般存储的是什么东西,也无非就是一些临时变量,方法返回值以及动态链表的引用等数据,那么造成栈溢出的最常见的可能就是运算分死循环或者说是一种递归调用!

jvm内存回收策略
jvm回收策略.png

jvm的回收策略的话,我是相对清晰的,因为自己学这个也有从去年年底开始也有一部分时间,近阶段在公司也碰巧用会这些也就实战了一把!
jvm的回收策略大致分为四大模块:
1.大对象直接进入老年代
大对象直接进入老年代,这个意思根据你的项目内存分析,配置jvm参数,当对象的创建大小大于这个阈值的时候,对象将不再新生代存活,直接进入老年代。
配置参数:-xx:PretenureSizeThreshold=n(单位bit)
在此,为大家写一个例子
在此之前先为大家介绍一下查看jvm参数的基本的一下参数配置:
-XX:+PrintGCDetails --->这个参数是输出jvm对的内存参数详情打印Console
-XX:+PrintCommandLineFlags -version --->这个主要是获取jvm的版本和默认的gc垃圾回收器以及默认的参数配置等

实例1

先配置这样的两个参数进行分析


jvm参数参数分析.png

运行结果分析:


运行结果.png

①图中,红色为-XX:+PrintCommandLineFlags -version配置输出
可以分析出:我们的jvm虚拟机版本是:

sun公司的HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)
此虚拟机默认的gc回收器是:-XX:+UseParallelGC
新声代:Parellal Scavenge
老年代:Parellal Old这两个gc回收器
②另一种颜色就是-XX:+PrintGCDetails输出结果
从数据可以看出主要是:

Heap
 PSYoungGen      total 54272K, used 2796K [0x0000000783b80000, 0x0000000787800000, 0x00000007c0000000)
  eden space 46592K, 6% used [0x0000000783b80000,0x0000000783e3b038,0x0000000786900000)
  from space 7680K, 0% used [0x0000000787080000,0x0000000787080000,0x0000000787800000)
  to   space 7680K, 0% used [0x0000000786900000,0x0000000786900000,0x0000000787080000)
 ParOldGen       total 124416K, used 0K [0x000000070b200000, 0x0000000712b80000, 0x0000000783b80000)
  object space 124416K, 0% used [0x000000070b200000,0x000000070b200000,0x0000000712b80000)
 Metaspace       used 2250K, capacity 4480K, committed 4480K, reserved 1056768K
  class space    used 244K, capacity 384K, committed 384K, reserved 1048576K

我们不难看出,因为对象想进入老年代的话,jvm默认是要经历15次MinorGC的,图中我们没有做堆内存的限制,因此程序比较小,没有发生gc回收的情况,接下来我们限制一下新生代的大小:

-Xms100m -Xmx100m -Xmn10m
参数配置.png

由于新生的对象会先进入Eden区,这是我们的新生代Eden:s0:s0是8m:1m:1m;并且设置大对象直接进入老年代的阈值我们故意创建一个

/**
 * java
 *  gc   策略测试
 */
public class JavaGcDetails {

    private static final String GC_MAIN="gcDetails";

    private static final Integer _1MB =1024*1024;

    public void test(){
        System.out.printf("gc details");
    }

    /**
     * jvm 配置参数 -Xms100m -Xmx100m -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8
     * -XX:PretenureSizeThreshold=3145728
     * @param args
     */
    public static void main(String[] args) {
        String [] arg1,arg2,arg3;
        arg1 = new String [4 * _1MB];
    }

运行结果:


执行结果.png

从结果中我们可以看出,这个对象进入了老年代!
2.不死小强进入老年代(长期存活的对象进入老年代)
长期存活对象进入老年代,用此参数限制
-XX:MaxTenuringThreshold=n
配置之后表示 新生代经历了多少次MinorGC之后的对象进入老年代

/**
 * java
 *  gc   策略测试
 */
public class JavaGcDetails {

    private static final String GC_MAIN="gcDetails";

    private static final Integer _1MB =1024*1024;

    public void test(){
        System.out.printf("gc details");
    }

    /**
     * jvm 配置参数 -Xms100m -Xmx100m -Xmn10m -XX:+PrintGCDetails -XX:SurvivorRatio=8
     * -XX:PretenureSizeThreshold=3145728
     * -XX:MaxTenuringThreshold=1
     * @param args
     */
    public static void main(String[] args) {
        String [] arg1,arg2,arg3;
        arg1 = new String [4 * _1MB];
        arg2 = new String [4 * _1MB];
        arg3 = new String [4 * _1MB];
    }

执行结果:


执行结果.png

3.对象年龄的动态判断
如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
4.空间分配担保
HandlePromotionFailure,检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC;如果小于,或者设置不允许冒险,那这时也要改为进行一次Full GC。
① 在MinorGC之前,检查老年代最大可用连续空间是否大于新生代所有对象的大小。
② 执行MinorGC
③ 如果空间不够。
④ 检查HandlePromotionFailure是否开启。
⑤ 如果没有开启。这个时候执行Full GC。
⑥ 如果这个参数开启,检查检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小。
⑦ 如果大于,试着执行MinorGC。
⑧ 如果小于,执行Full GC。


空间分配担保.png

在此基础之上大家就可以针对web工程进行内存的分析和漫长的调优过程了,给大家附上一张,我整理的jvm内存参数表,大家可以对应没事检测使用!

内存管理参数
DisableExplicitGC   默认关闭    忽略来自System.gc()方法触发的垃圾收集
ExplicitGCInvokesConcurrent 默认关闭    当收到System.gc()方法提交的来机收集申请时,使用CMS收集器进行收集
UseSerialGC Client模式的虚拟机默认开启,其他模式关闭 虚拟机运行在Client模式下的默认值,打开此开关后,使用Serial + Serial Old的收集器组合进行内存回收
UseParNewGC 默认关闭    打开此开关后,使用ParNew + Serial Old的收集器组合进行内存回收
UseConcMarkSweepGC  默认关闭    打开此开关后,使用ParNew + CMS + Serial Old的收集器组合进行内存回收.如果CMS收集器出现Concurrent Mode Failure,则Serial Old收集器将作为后备收集器
UseParallelGC   Server模式的虚拟机默认开启,其他模式关闭 虚拟机运行在Server模式下的默认值,打开此开关后,使用Parallel Scavenge + Serial Old的收集器组合进行内存回收
UseParallelOldGC    默认关闭    打开此开关后,使用Parallel Scavenge + Parallel Old的收集器组合进行内存回收
SurvivorRatio   默认为8    新生代中Eden区域与Survivor区域的容量比
PretenureSizeThreshold  无默认值    直接晋升到老年代的对象大小,设置这个参数后,大于这个参数的对象将直接在老年代分配
MaxTenuringThreshold    默认值为15  晋升到老年代的对象年龄,每个对象在坚持过一次Minor GC之后,年龄就+1,当超过这个参数值时就进入老年代
UseAdaptiveSizePolicy   默认开启    动态调整java堆中各个区域的大小及进入老年代的年龄
HandlePromotionFailure  jdk1.5及以前是默认关闭,jdk1.6默认开启   是否允许分配担保失败,即老年代的剩余空间不足以应付新生代的整个Eden和Survivor区的所有对象都存活的极端情况
ParallelGCThreads   少于或等于8个CPU时默认值为CPU数量值,多于8个CPU时比CPU数量值小  设置并行GC时进行内存回收的线程数
GCTimeRatio 默认值99   GC时间占总时间的比率.仅在使用Parallel Scavenge收集器时生效
MaxGCPauseMills 无默认值    设置GC最大停顿时间.仅在使用Parallel Scavenge收集器时生效
CMSInitiatingOccupancyFraction  默认值68   设置CMS收集器在老年代空间被使用多少后触发垃圾收集
UseCMSCompactAtFullCollection   默认开启    设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片整理
CMSFullGCsBeforeCompaction  无默认值    设置CMS收集器在进行若干次垃圾收集后再启动一次内存碎片整理
ScavengeBeforeFullGC    默认开启    在Full GC发生之前触发一次Minor GC
UseGCOverheadLimit  默认开启    禁止GC过程无限制的执行,如果过于频繁,就直接发生OutOfMemory
UseTLAB Server模式默认开启    优先在本地线程缓冲区中分配对象,避免分配内存时的锁定过程
MaxHeapFreeRatio    默认值70   当Xmx值比Xms值大时,堆可以动态收缩和扩展,这个参数控制当堆空闲大于指定比率时自动收缩
MinHeapFreeRatio    默认值40   当Xmx值比Xms值大时,堆可以动态收缩和扩展,这个参数控制当堆空闲小于指定比率时自动收缩
MaxPermSize 大部分情况下默认值是64MB  永久代的最大值
即时编译参数
CompileThreshold    Client模式下默认值1500,Server模式下默认值10000  触发即时编译的阈值
OnStackReplacePercentage    Client模式下默认值933,Server模式下140    OSR比率,它是OSR即时编译阈值计算公司的一个参数,用于代替BackEdgeThreshold参数控制回边计数器的实际溢出阈值
ReservedCodeCacheSize   大部分情况下默认值32MB   即时编译器编译的代码缓存使得最大值
类型加载参数
UseSplitVerifier    默认开启    使用依赖StackMapTable信息的类型检查代替数据流分析,以加快字节码校验速度
FailOverToOldVerifier   默认开启    当类型校验失败时,是否允许回到老的类型推到校验方式进行校验,如果开启则允许
RelaxAccessControlCheck 默认开启    在校验阶段放松对类型访问性的限制
多线程相关参数
UseSpinning jdk1.6默认开启,jdk1.5默认关闭   开启自旋锁以免线程频繁的挂起和唤醒
PreBolckSpin    默认值10   使用自旋锁时默认的自旋次数
UseThreadPriorities 默认开启    使用本地线程优先级
UseBiasedLocking    默认开启    是否使用偏向锁,如果开启则使用
UseFastAccessorMethods  默认开启    当频繁反射执行某个方法时,生成字节码来加快反射的执行速度
性能参数
AggressiveOpts  jdk1.6默认开启,jdk1.5默认关闭   使用激进的优化特征,这些特征一般是具备正面和负面双重影响的,需要根据具体应用特点分析才能判定是否对性能有好处
UseLargePage    默认开启    如果可能,使用大内存分页,这项特性需要操作系统的支持
LargePageSizeInBytes    默认值4MB  使用指定大小的内存分页,这项特性需要操作系统的支持
StringCache 默认开启    是否使用字符串缓存,开启则使用
调试参数
HeapDumpOnOutOfMemoryError  默认关闭    在发生内存溢出异常时是否生成堆转储快照,关闭则不生成
OnOutOfMemoryError  无默认值    当虚拟机抛出内存溢出异常时,执行指令的命令
OnError 无默认值    当虚拟机抛出ERROR异常时,执行指令的命令
PrintClassHistogram 默认关闭    使用[ctrl]-[break]快捷键输出类统计状态,相当于jmap-histo的功能
PrintConcurrentLocks    默认关闭    打印J.U.C中的状态
PrintCommandLineFlags   默认关闭    打印启动虚拟机时输入的非稳定参数
PrintFlagsFinal ----    显示所有可设置的参数及它们的值(***从JDK 6 update 21开始才可以用)
PrintFlagsInitial   ----    显示在处理参数之前所有可设置的参数及它们的值,然后直接退出程序
PrintCompilation    默认关闭    打印方法即时编译信息
PrintGC 默认关闭    打印GC信息
PrintGCDetails  默认关闭    打印GC的详细信息
PrintGCTimeStamps   默认关闭    打印GC停顿耗时
PrintTenuringDistribution   默认关闭    打印GC后新生代各个年龄对象的大小
TraceClassLoading   默认关闭    打印类加载信息
TraceClassUnloading 默认关闭    打印类卸载信息
PrintInlining   默认关闭    打印方法内联信息
PrintCFGToFile  默认关闭    将CFG图信息输出到文件,只有DEBUG版虚拟机才支持此参数
PrintIdealGraphFile 默认关闭    将Ideal图信息输出到文件,只有DEBUG版虚拟机才支持此参数
UnlockDiagnosticVMOptions   默认关闭    让虚拟机进入诊断模式,一些参数(如PrintAssembly)需要在诊断模式中才能使用
PrintAssembly   默认关闭    打印即时编译后的二进制信息

上述给大家介绍了jvm内存分配和回收策略,欢迎打击一起讨论学习。
谢谢大家!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容