博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java内存结构和GC垃圾回收
阅读量:2440 次
发布时间:2019-05-10

本文共 4130 字,大约阅读时间需要 13 分钟。

文章目录

Java 内存结构

Java内存模型和多线程JMM相关

Java内存结构和JVM虚拟机存储空间相关

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZyfpDHrj-1577000962927)(E:\研究生\面试知识\java内存结构.jpg)]

  • 方法区

    什么是方法区:也称永久区,static关键词修饰、常量信息;当class文件被加载的时候,就会被初始化;

    调优问题

    1. 不能定义太多常量,不然垃圾回收机制是不会回收方法区的,占用内存,产生内存溢出问题
    2. 所有线程会共享静态变量,要注意线程安全问题
  • 堆(参数调优重点关注)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vbmiPXjQ-1577000962928)(E:\研究生\面试知识\堆.jpg)]

    什么是堆:new创建的对象、数组等都会存放在堆中,所有线程会被共享;堆内存中分配了两个区:新生代和老年代(分代的目的是为了垃圾回收机制)

    • 新生代

      刚创建的对象,先存放在新生代中,分为eden、s0、s1区

      • 刚创建的对象会存储在eden区
      • s0区和s1区大小相等
      • 垃圾回收机制主要回收新生代
    • 老年代

      如果对象在频繁的使用,则放在老年代

    • 堆的参数配置

      -XX :+PrintGC 每次触发GC的时候打印相关日志

      -XX:+UserSerialGC 串行回收

      -XX:+PrintGCDetails 更详细的GC日志

      -Xms 堆初始值

      -Xmx 堆最大可用值尽量减少垃圾回收次数

      -Xmn 新生代堆最大可用值

      -XX:SurvivorRatio 用来设置新生代中eden空间和from/to空间的比例为n/1

      总结:在实际工作中,我们可以直接将初始的堆大小与最大堆大小相等,这样的好处是可以减少程序运行时垃圾回收次数,从而提高效率

      -XX:NewRatio=2 总结:不同的分布情况,对系统执行会产生一定的影响,在实际工作中,应根据系统的特点做出合理的配置,基本策略:尽可能将对象留在新生代,减少老年代的GC次数。除了可以设置新生代的绝对大小(-Xmn),可以使用(-XX:NewRatio)设置新生代和老年代的比例:-XX:NewRatio=老年代/新生代

  • 什么是栈:类的方法;局部变量,代码运行完毕自动释放,每个线程私有,互不共享,不会产生线程安全问题

  • 本地方法栈

    作用:主要调用c语言,android中会用到;

jvm参数调优
  1. 堆初始值与堆内存的最大值一定要保持一致
    • 减少垃圾回收机制次数
  2. 设置新生代与老年代的回收比例
    • 新生代:老年代=1/3 或者1/4
堆溢出
  • 错误原因

    java.lang.OutOfMemoryError : Java heap space 堆内存溢出

  • 解决办法

    设置堆内存大小 -Xms1m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError

  • Tomcat 内存溢出在catalina.sh修改JVM堆内存大小

    JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxNewSize=512m"
栈溢出
  • 错误原因

    java.lang.StackOverflowError 栈内存溢出,栈溢出产生于递归调用,循环遍历是不会的,但是循环方法里面产生的递归调用,也会发生栈溢出,

  • 解决办法

    设置线程最大调用深度

    -Xss5m 设置最大调用深度

JVM参数调优总结

在JVM启动参数中,可以设置跟内存、垃圾回收相关的一些参数设置,默认情况不做任何设置JVM会工作的很好,但对一些配置很好的Server和具体的应用必须仔细调优才能获得最佳性能。通过设置我们希望达到的一些目标:

  • GC的时间足够小
  • GC的次数足够少
  • 发生Full GC(新生代和老生代)的周期足够的长

前两个目前是相悖的,要想GC时间小必须要一个更小的堆,要保证GC次数足够小,必须保证一个更大的堆,我们只能取其平衡

垃圾回收机制

垃圾回收机制不定时,向堆内存清理不可达对象。其简要过程如下:

不可达对象并不会马上被直接回收,而是至少要经过两次标记的过程。第一次被标记的对象,会检查该对象是否重写了finalize()方法。如果重写了该方法,则将其放入一个F-query队列中,否则,直接将对象加入“即将回收”集合。在第二次标记之前,F-Query队列中的所有对象会逐个执行finalize()方法,但是不保证该队列中所有对象的finalize()方法都能被 执行,这是因为JVM创建一个低优先级的线程去运行此队列中的方法,很可能在没有遍历完之前,就已经被剥夺了运行的权利。那么运行finalize()方法的意义何在呢?这是对象避免自己被清理的最后手段;如果在执行finalize()方法过程中,使得此对象重新与GC Roots引用链相连,则会在第二次标记过程中将此对象从F-Query队列中清除,避免在这次回收中被清除,恢复成一个“正常”的对象。但显然这种好事不能无限的发生,对于曾经执行过一次finalize()的对象来说,之后如果再被标记,则不会再执行finalize()方法,只能等待被清除的命运。

之后,GC将对F-Queue中的对象进行第二次小规模的标记,将队列中重新与GC Roots引用链恢复连接的对象清除出“即将回收”集合。所有此集合中的内容将被回收。

内存溢出和内存泄露
  • 内存溢出

    需要4g,只有3g

  • 内存泄露

    对象已经没有被应用程序使用,但是垃圾回收器没办法移除他们,因为还在被引用着。

调优策略
  1. 尽量减少垃圾回收次数
  2. 尽量减少常量信息
  3. 新生代回收次数比老年代多
Java垃圾回收算法
  • 标记清除法–碎片化(一般不用)

    标记清除算法为每个对象做一个标记,0可达,1不可达,对象没有经常使用,将对象标记为1不可达,容易造成碎片化

  • 标记压缩–老生代

    先进行排序,再进行删除,不会产生碎片

  • 引用计数法–新生代

    每个对象都会有一个标记,默认是15,gc回收的时候,对象不可达则减一,可达则加一,直到机会为0进行回收,如果超过15,则进入s0区或者s1区;

    优点:引用计数收集器可以很快的执行,交织在程序运行中,对程序需要不被长时间打断的实时环境比较有利。

    缺点:无法检测出循环引用,如父对象有一个对子对象的应用,子对象反过来引用父对象。这样,他们的引用计数永远不可能为0,而且每次加非常浪费内存。

  • 复制算法–新生代(老年代没有)

    只有一个区域可以存活,只需要移动堆顶指针,按顺序分配内存即可

    优点:连续性,不会产生碎片化,运行高效

    缺点:课使用内存降为原来一半

  • 分代算法–新生代和老年代

垃圾回收的时候会出现停顿现象,垃圾回收的任务是识别和回收垃圾对象进行内存清理,为了让垃圾回收器可以进行更高效的执行,大部分情况下,会要求系统进入一个停顿的状态。停顿的目的是为了终止所有的应用线程,只有这样的系统才不会有新垃圾的产生。同时停顿保证了系统状态在某一瞬间的一致性,也有利于更好的标记垃圾对象。因此在垃圾回收时,都会产生应用程序的停顿

垃圾收集器

串行回收器

单线程执行回收操作,回收期间暂停所有应用线程的执行,client模式下的默认回收器,通过-XX:+UseSerialGC命令行可选项强制执行

并行回收器(多线程回收垃圾)

并行回收器在串行回收器的基础上做了改进,可以使用多个线程同时进行垃圾回收,对于计算能力强的计算机而言,可以有效缩短垃圾会后所需要的间际时间

CMS

CMS回收器(Concueernt Mark Sweep Collector)是回收停顿时间比较短、目前比较常用的垃圾回收器。它通过

  • 初始标记(Initial Mark)
  • 并发标记(Concurrent Mark)
  • 重新标记(Remark)
  • 并发清除(Concurrent Sweep)

四个步骤完成垃圾回收,第一和第三步还是会造成STW(Stop The World)即垃圾回收的某个阶段会暂停整个应用程序的执行,而第2、4步的并发标记和并发清除两个阶段可以和应用程序并发执行,也是比较耗时的操作,但并不影响整个程序的正常执行。

由于CMS采用的是标记清除算法,因此会产生大量的空间碎片,为了解决这个这个问题,CMS可以通过配置**-XX:+UserCMSCompactAtFullCollection参数,强制JVM在FGC完成后对老年代进行压缩,执行一次碎片化整理,但是空间碎片整理阶段也会引发STW。为了减少STW次数,CMS还可以通过配置-XX:CMSFullGCsBeforeCompaction=n**参数,在执行n次FGC后,JVM再在老年代执行空间碎片整理。

G1(Garbage-First Garbage Colletor)

Hotspot在JDK7中推出了新一代G1垃圾回收,通过-XX:+UseG1GC参数启用。和CMS相比,G1具有压缩功能,能避免碎片化问题,G1的暂停时间更加可控

G1将Java堆空间分割为若干相同大小的区域,即region,包括Eden(年轻代)、Survivor(年轻代)、Old(老年嗲)、Humongous(巨型对象区域)四种类型。其中,Humongous是特殊的Old类型,专门放置大型对象。这样的划分方式意味着不需要一个连续的存储空间管理对象。G1将空间分为多个区域,优先回收垃圾最多的区域。G1采用的是“Mark-Copy(标记整理)”,有非常好的空间整合能力,不会产生大量的空间碎片。G1的一大优势在于可预测的停顿时间,能够尽可能快的在指定的时间内完成垃圾回收任务。在JDK11中,已经将G1设为默认垃圾回收器,通过jstat命令可以查看垃圾回收情况。

G1的整理标记和CMS一样,分为5个步骤

  • 初始标记(Initial Mark)) 该阶段会引起STW,会标记GC Roots直接可达的存活对象
  • 根区域扫描(Root Region Scan) 该阶段不会引起STW,它会并发地从上一阶段标记的存活区域中扫描被引用的老年代对象
  • 并发标记(Concurrent Mark) 从堆中标记存活的对象,与CMS类似
  • 重新标记(Remark) 该阶段会引起STW,与CMS类似,会完成最终标记处理
  • 清除(Cleanup) 改阶段会统计所有区域中的存活对象,并将待回收区域按照回收价值排序,优先回收垃圾最多的区域

转载地址:http://cgcqb.baihongyu.com/

你可能感兴趣的文章
持续改进之配置管理变更的关键路径
查看>>
mongodb replica sets 测试
查看>>
linux AS6.2 与 as5.4 的对比,性能提升明显
查看>>
FLASHCACHE 的是是非非
查看>>
99-lisp lisp 的99个问题 P1-10
查看>>
PG 函数的易变性(Function Volatility Categories)
查看>>
Lisp Quote 和Backquote分析
查看>>
PG psql 变彩色显示
查看>>
SICP 练习 1.3
查看>>
pg 数据库HA 启动脚本的两个假设
查看>>
sql_log_bin在GTID复制下的一个现象
查看>>
双主+haproxy手工切换的一个注意点
查看>>
利用binlog2sql实现闪回
查看>>
LTP(Linux Test Project)学习(一)——LTP介绍
查看>>
Linux 4.0亮点特性
查看>>
Linux 4.1亮点特性
查看>>
Linux 4.5 亮点特性
查看>>
CGI
查看>>
csv文件
查看>>
XML CDATA
查看>>