JVM学习

jvm内存区域与内存溢出异常

在这里插入图片描述
在这里插入图片描述

当内存不够 出现了OutOfMemoryError简称为OOM异常
解决这个问题先用内存映像分析工具先看看内存的对象是否是必要的,也就是说先看看是内存泄漏还是内存溢出
如果是内存泄露,通过工具进一步查找GC Roots的引用链
内存泄露
内存泄漏是指由于疏忽或错误造成程序未能释放不用的内存,该回收的不回收。
内存泄漏随着被执行的次数越多-最终会导致内存溢出。
而因程序死循环导致的不断创建对象-只要被执行到就会产生内存溢出。

内存泄漏常见几个情况:
静态集合类
声明为静态(static)的 HashMap、Vector 等集合
通俗来讲 A 中有 B,当前只把 B 设置为空,A 没有设置为空,回收时 B 无法回收-因被 A 引用。

监听器
监听器被注册后释放对象时没有删除监听器

物理连接
DataSource.getConnection()建立链接,必须通过 close()关闭链接

内部类和外部模块等的引用
发现它的方式同内存溢出,可再加个实时观察
jstat -gcutil 7362 2500 70
虚拟机栈和本地方法栈内存溢出
1.StackOverFlow 栈深度大于虚拟机允许最大深度
2.OutOfMeroryError 虚拟机扩展栈时无法申请足够的内存
方法区和运行时常量池溢出
方法区(Method Area)
方法区也称”永久代“,它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。
随着JDK8的到来,JVM不再有 永久代(PermGen)。但类的元数据信息(metadata)还在,只不过不再是存储在连续的堆空间上,而是移动到叫做“Metaspace”的本地内存(Native memory)。

垃圾收集器与内存分配策略
1.引用计数法
对象引用一次加一,引用失效一次减一,当计数为0,就是不能使用的对象了,主流的Java虚拟机并没用用这个方法,因为很难解决这个互相引用的问题
在这里插入图片描述
这样的就不会被回收了
2.可达性分析算法
在这里插入图片描述
从根节点开始,依次往下遍历,有向图算法,如果不可达,那就都回收,可达性分析算法会发现对象与GC Roots没用引用链,将会呗第一次筛选标记,看看是否执行finalize方法,如果没有或者已经呗调用,那就视为无必要执行,如果可以执行那就加入一个F queue,随后会在虚拟机创建的低线程来执行,如果出现了第二次标记,则回收。
值得注意的是这个finalize方法只能被系统调用一次,如果调用第二次就会失败
比如下面的第二次调用会失败,但尽量用try-finalize比较好不用finalize
在这里插入图片描述

引用类型:
1.强引用:Object object = new Object()
2.软引用:有用但非必须对象,内存溢出异常之前进行二次回收,如果没用足够内存,那就内存溢出异常
3.弱引用:非 必须对象,在下一次垃圾回收之前,一定会回收只被弱引用关联的对象
4.虚引用:设置对象虚引用能回收到一个系统的通知

垃圾回收部分,方法区性价比比较差,在堆中,新生代,一次垃圾收集可以回收70%-95%空间,而永久代的垃圾回收效率远低于此。
永久带回收:废弃常量和费用的类

垃圾收集算法
1.标记清除算法:将要清除的对象打标记然后统一清除,不足是效率和空间占内存 可用2
在这里插入图片描述
2.复制算法:内存分两半,当一块内存用完了,将存活的对象复制到另一半,把使用过的内存空间清理掉
商业虚拟机基本用此方法回收新生代,因为新生代大部分朝生夕死,内存分大Eden设为A和两块小的survivor设为B,C,比例8:1:1,每次使用的时候用AB,存活的放c,然后清理AB 但有个缺点就是对象存活数量较多的时候 复制操作 效率差 可用3
在这里插入图片描述
3.标记整理算法
先标记,然后让存活对象向一端移动,清理边界以外的内存
在这里插入图片描述
4.分代收集算法
商业虚拟机的垃圾收集用分代收集,新生代死的多,用复制收集算法,老年存货率较高,用标记-清(整)理算法
GC日志读法
在这里插入图片描述
33.125和100.667 :时间,从java虚拟机启动一来经历的秒
GC和FULL GC:表示停顿类型
DefNew Perm Tenured PSYoungGen:表示区域名字,Serial PerNew Parallel Scavenge由收集器决定的
方括号内3324k->152k:GC前该内存区域使用的容量3324k和GC后该内存区域使用的容量152k
方括号外3324k->152k:GC前java堆使用的容量3324k和GC后java堆使用的容量152k
0.0025925:该内存区域GC所占用时间
垃圾收集器参数总结:
在这里插入图片描述在这里插入图片描述
垃圾回收器列表:
在这里插入图片描述

深入理解java虚拟机第四章有很多jdk自带的工具进行反编译的
垃圾收集器
在这里插入图片描述
类加载过程
在这里插入图片描述
jvm调优目的
1.减少Full Gc次数

  • (Tenured)老年代被写满:让对象在新生代GC时被回收、让对象在新生代多存活一段时间和不要创建过大的对象及数组避免直接在旧生代创建对象 。

  • (Pemanet Generation)持久代空间不足:增大Perm Gen空间,避免太多静态对象 , 控制好新生代和旧生代的比例

  • System.gc()显示调用:垃圾回收不要手动触发,尽量依靠JVM自身的机制
    2.减小Gc频率 将进入老年代的对象数量降到最低
    jvm性能调优方法和步骤

  • 监控GC状态:使用工具,查看日志,分析jvm参数设置,根据各区域内存划分和GC执行时间,判断是否优化

  • 生成堆dump文件:用java的jmap命令来生成文件

  • 分析dump文件:在Linux上,用工具打开文件

  • 分析结果,判断是否需要优化:gc超过1-3s,gc频率高,则优化
    满足下面条件则不需要优化:

    • Minor GC执行时间不到50ms;
    • Minor GC执行不频繁,约10秒一次;
    • Full GC执行时间不到1s;
    • Full GC执行频率不算频繁,不低于10分钟1次;
  • 调整GC类型和内存分配:内存过大或过小,采用的GC收集器较慢,优先调整这些参数,对比

  • 不断分析调整:不断实验和试错,找到最合适参数
    在这里插入图片描述
    jvm调优参
    1、 大多数的java应用不需要GC调优
    2、 大部分需要GC调优的的,不是参数问题,是代码问题
    3、 在实际使用中,分析GC情况优化代码比优化GC参数要多得多;
    4、 GC调优是最后的手段
    GC调优的最重要的三个选项:
    第一位:选择合适的GC回收器
    第二位:选择合适的堆大小
    第三位:选择年轻代在堆中的比重

    1.针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,通常把最大、最小设置为相同的值;
    2.年轻代和年老代将根据默认的比例(1:2)分配堆内存, 可以通过调整二者之间的比率NewRadio来调整二者之间的大小,也可以针对回收代。比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小。
    3.年轻代和年老代设置多大才算合理1)更大的年轻代必然导致更小的年老代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的年老代会导致更频繁的Full GC2)更小的年轻代必然导致更大年老代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率如何选择应该依赖应用程序对象生命周期的分布情况: 如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,年老代应该适当增大。但很多应用都没有这样明显的特性。在抉择时应该根 据以下两点:
    (1)本着Full GC尽量少的原则,让年老代尽量缓存常用对象,JVM的默认比例1:2也是这个道理 。
    (2)通过观察应用一段时间,看其他在峰值时年老代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1。但应该给年老代至少预留1/3的增长空间。
    4.在配置较好的机器上(比如多核、大内存),可以为年老代选择并行收集算法: -XX:+UseParallelOldGC 。
    5.线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太大了,一般256K就足用。
    理论上,在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。

jvm内存结构:
在这里插入图片描述
此图结构由下到上分配的
新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 )
默认的,Eden : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。

方法区或永生代相关设置

-XX:PermSize=64MB 最小尺寸,初始分配
-XX:MaxPermSize=256MB 最大允许分配尺寸,按需分配
XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled 设置垃圾不回收
默认大小
-server选项下默认MaxPermSize为64m
-client选项下默认MaxPermSize为32m

在这里插入图片描述
-Xms设置堆的最小空间大小。
-Xmx设置堆的最大空间大小。
-Xmn:设置年轻代大小
-XX:NewSize设置新生代最小空间大小。
-XX:MaxNewSize设置新生代最大空间大小。
-XX:PermSize设置永久代最小空间大小。
-XX:MaxPermSize设置永久代最大空间大小。
-Xss设置每个线程的堆栈大小
-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
-XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。

java-Xmx3550m-Xms3550m-Xmn2g-Xss128k
-XX:ParallelGCThreads=20
-XX:+UseConcMarkSweepGC-XX:+UseParNewGC
解析:
-Xmx3550m:设置JVM最大可用内存为3550M。
-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小+年老代大小+持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,官方推荐配置为整个堆的3/8。
-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
jvm其他调优配置:
-server JVM运行的模式之一, server模式才能进行逃逸分析, JVM运行的模式还有mix/client
-Xmx10m和-Xms10m:堆的大小
-XX:+DoEscapeAnalysis:启用逃逸分析(默认打开)
-XX:+PrintGC:打印GC日志
-XX:+EliminateAllocations:标量替换(默认打开)
-XX:-UseTLAB 关闭本地线程分配缓冲
-XX:+EliminateLocks可以开启同步消除,进行测试执行的效率
对栈上分配发生影响的参数就是三个,-server、-XX:+DoEscapeAnalysis和-XX:+EliminateAllocations,任何一个关闭都不会发生栈上分配,因为启用逃逸分析和标量替换默认是打开的,所以,在我们的例子中,JVM的参数只用-server一样可以有栈上替换的效果

内存分配与回收策略

  1. 对象优先在Eden分配,如果说Eden内存空间不足,就会发生Minor GC
  2. 大对象直接进入老年代,大对象:需要大量连续内存空间的Java对象,比如很长的字符串和大型数组
  3. -XX:PretenureSizeThreshold 参数 ,大于这个数量直接在老年代分配,缺省为0 ,表示绝不会直接分配在老年代。
  4. 长期存活的对象将进入老年代,默认15岁,-XX:MaxTenuringThreshold调整
  5. 动态对象年龄判定,为了能更好地适应不同程序的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄
  6. 空间分配担保:新生代中有大量的对象存活,survivor空间不够,当出现大量对象在MinorGC后仍然存活的情况(最极端的情况就是内存回收后新生代中所有对象都存活),就需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代.只要老年代的连续空间大于新生代对象的总大小或者历次晋升的平均大小,就进行Minor GC,否则FullGC。

jvm垃圾回收器搭配
如果是线上系统,比价看重服务的响应速度,则建议使用:ParNew+CMS + Serial Old(替补)
如果是跑批系统,比较看重系统的吞吐量,则建议使用:Parallel Scavenge+Parallel Old

在这里插入图片描述
垃圾收集器参数总结:
-XX:+UseSerialGC:在新生代和老年代使用串行收集器
-XX:+UseParNewGC:在新生代使用并行收集器
-XX:+UseParallelGC :新生代使用并行回收收集器,更加关注吞吐量
-XX:+UseParallelOldGC:老年代使用并行回收收集器
-XX:ParallelGCThreads:设置用于垃圾回收的线程数
-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器
-XX:ParallelCMSThreads:设定CMS的线程数量
-XX:+UseG1GC:启用G1垃圾回收器

jvm日志构成vm调优更多学习看知乎文档:https://zhuanlan.zhihu.com/p/62762455
在这里插入图片描述
内存溢出解决方案:
OutOfMemoryError:Java heap space - 堆空间溢出
OutOfMemoryError:PermGen space - 方法区和运行时常量池溢出
OutOfMemoryError:unable to create new native thread - 线程过多
OutOfMemoryError:PermGen space

  1. 清理应用程序中 WEB-INF/lib 下的 jar,用不上的 jar 删除掉,多个应用公共的 jar 移动到 Tomcat 的 lib 目录,减少重复加载。
  2. JDK8 以后把原本放在永久代的字符串常量池移出, 放在 Java 堆中(元空间 Metaspace)中,元数据并不在虚拟机中,使用的是本地的内存。使用 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 控制元空间大小。
    OutOfMemoryError:Java heap space 表示堆空间溢出
    (1)使用 jmap 或 -XX:+HeapDumpOnOutOfMemoryError 获取堆快照。 (2)使用内存分析工具(visualvm、mat、jProfile 等)对堆快照文件进行分析。 (3)根据分析图,重点是确认内存中的对象是否是必要的,分清究竟是是内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值