JavaGC

Java GC

GC(Garbage Collection),目的是为了防止内存泄漏,更加有效的利用内存把一些长时间不用对象从内存中移除(一下内容为JDK8 HotSpot JVM)

特点

  • stop-the-world

参考资料

定义垃圾

1.引用计数算法

2.可达性分析算法

引用计数算法

通过在对象头用一个值来记录被引用的次数,如果应用次数为0则被定义为垃圾可被回收。(被引用了+1,取消应用-1)

String s = new String("value");
  1. 引用次数+1,引用次数为 1
s = null
  1. 引用次数 -1,引用次数为 0

特点

  1. 在整个应用运行过程中,无时无刻都在记录着引用
  2. 如果记录两个对象相互引用,那么无法被认为是垃圾

两个对象相互引用DEMO

public class A {
	public String name;
	public A value;
	public A(String name) {
		this.name = name;
	}
}

// main
A a = new A("one")
A b = new A("two")

a.value = b;
b.value = a;

a = null
b = null;

  1. 创建了两个对象one,two且分别让变量a,b指向one,two对象
  2. 在让one,two两个对象里面的属性相互指向,这时他们的引用次数为 2
  3. 移除变量a,b的引用,此时one,two引用次数为1,但是他们因为失去了变量的引用,应用里无法再找打这两个对象。通过引用计数算法,也就无法再被回收

可达性分析算法

通过一些被称为引用链(GC Root)的对象作为起点,从这些节点开始向下搜索,当一个对象到 GC Roots 没有任何引用链相连时,则证明该对象是不可用的。

这个方案解决了对象相互引用的问题,但是又有一个问题,哪些对象是GC Root

GC Root

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象(类信息在方法区)
  • 方法区中常量引用的对象
  • 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象

虚拟机栈(栈帧中的本地变量表)中引用的对象

public static void testGC(){
	Sring s = new String("object");
	s = null;
}
  1. 变量sGC Root,当变量s为空的时候,对象object也就断了和GC Root的引用链

方法区中类静态属性引用的对象

public class A {
	public static A value;
	public String name;
	public A(String name) {
		this.name = name;
	}
}

// main
public static void testGC(){ 
	A s = new A("object");
	s.value = new A("object2");
	s = null;
}

  1. 变量s,静态属性value都为GC Root

方法区中常量引用的对象

public class A {
	public static final A value = new A("obj");
	public String name;
	public A(String name) {
		this.name = name;
	}
}

// main
public static void testGC(){ 
	A s = new A("object");
	s = null;
}

  1. 变量s,静态常量属性value都为GC Root

回收垃圾

标记-清除算法 复制算法 标记整理算法 分代回收算法

标记 - 清除算法

  1. 第一步:标记哪些对象可被回收
  2. 第二步:回收标记了的对象,清理出内存

特点

  1. 有很严重的内存碎片问题

复制算法

标记-清除算法演变而来,解决了内存碎片问题

  1. 把内存平分为两个区域,回收时先把活着的对象放到另一个内存区域中,然后把另一个内存区域清除

特点

  1. 使用过程中,内存浪费有些严重,100M内存有效使用只有50M
  2. 总有一个区域是不被使用的

标记整理算法

  1. 第一步:先标记无效对象
  2. 第二步:有效对象向一端移动(整理)
  3. 第三步:清理无效对象

特点

  1. 解决了标记-清理内存碎片问题
  2. 解决了复制算法内存空间只用一半问题
  3. 因为引用对象内存地址频繁变更,引用对象需要重新指定新的内存,使得效率上比复制算法差很多

分代收集算法

把内存分区,不同的区域存放不同的类型对象(按时间,内存大小),不同区域使用不同的GC策略,组合使用不同的回收算法

Java 堆内存模型与回收策略

使用分代收集算法

  1. JVM 堆内存主要分为新生代(YoungGen)和老年代(OldGen)。新生代又在分为Eden,Survivor两个区。Survivor又分为From,To 两个区域,From,To这两个区域的内存指向每Minor GC一次会发生一次置换

Eden 区 当Eden没有足够的内存进行分配的时候。会触发一次Minor GC,绝大部分对象会被清除,而一部分对象会被移动到Survivor区域中的From内存中,之后Enden会被清空

  1. Minor GC 相比 Major GC 更频繁,回收速度也更快
  2. 如果对象内存太大会直接进入Old,为了减少Survivor复制的开销

Survivor Survivor承担着Eden与Old一层缓冲的作用,目的是让只有经历多次Minor GC的对象才会被送到Old区域,而不是只经历了一次就被送到了Old Gen

  1. 为什么Survivor要分为From,To? 为了解决内存碎片化,每次 Minor GC,会将之前 Eden 区和 From 区中的存活对象复制到 To 区域。第二次 Minor GC 时,From 与 To 职责兑换,这时候会将 Eden 区和 To 区中的存活对象再复制到 From 区域,以此反复。

OId Gen 老年代占据着2/3的堆内存空间,只有在 Major GC的时候才会进行清理,每次 GC 都会触发“Stop-The-World”。并且内存越大,STW 的时间也越长。

在内存担保机制下,以下几种情况也会进入老年代。 1、大对象 需要大量连续内存空间的对象,为了避免在 Eden 区及2个 Survivor 区之间发生大量的内存复制。

2、长期存活对象 虚拟机给每个对象定义了一个对象年龄(Age)计数器。正常情况下对象会不断的在 Survivor 的 From 区与 To 区之间移动,对象在 Survivor 区中每经历一次 Minor GC,年龄就增加1岁。当年龄增加到15岁时,这时候就会被转移到老年代。当然,这里的15,JVM 也支持进行特殊设置。

3、动态对象年龄 如果 Survivor 空间中相同年龄所有对象大小的综合大于 Survivor 空间的一半,年龄大于等于该年龄的对象就可以直接进去老年区,无需等你“成年”。

HotSpot JVM 关于GC的配置参数

GC 种类

GC启用参数年轻代算法老年代算法
-XX:+UseSerialGCSerial (DefNew)Serial Old(PSOldGen)
-XX:+UseParallelGCParallel Scavenge (PSYoungGen)Serial Old(PSOldGen)
-XX:+UseParallelOldGCParallel Scavenge (PSYoungGen)Parallel Old (ParOldGen)
-XX:-UseParNewGCParNew (ParNew)Serial Old(PSOldGen)
-XX:+UseConcMarkSweepGCParNew (ParNew)CMS+Serial Old(PSOldGen)
-XX:+UseG1GCG1G1

查看当前项目使用的GC类型

  1. 查询java进程 jps
  2. 查询是否开启某一个GCjinfo -flag UseSerialGC java进程
    1. jinfo -flag可以查询控制布尔变量,是开启还是关闭
    2. 运行过程重开启GC日志jinfo -flag +PrintGC java 进程

GC日志详情参数

参数含义
-XX:+PrintGC打印GC日志,和 -verbose:gc 是相同的命令
-XX:+PrintGCDetails打印GC的详细日志
-XX:+PrintGCTimeStamps打印GC的时间戳(JVM启动到GC发生所经历的时间)
-XX:+PrintGCDateStamps打印GC的日期时间(如:2019-05-06T19:34:52.072+0800)
-XX:+PrintHeapAtGC打印GC前后的详细的堆信息
-XX:+PrintGCCause原因

GC日志输出

参数含义
-Xloggc:logs/gc.log.date +%Y-%m-%dGC日志输出到指定文件
-XX:+UseGCLogFileRotation开启滚动GC日志
-XX:GCLogFileSize=512mGC日志文件最大值
-XX:NumberOfGCLogFiles=5GC日志文件的个数
  1. 2014-07-18T16:02:17.606+0800(当前时间戳): 611.633(时间戳): [GC(表示Young GC) 611.633: [DefNew(单线程Serial年轻代GC): 843458K(年轻代垃圾回收前的大小)->2K(年轻代回收后的大小)(948864K(年轻代总大小)), 0.0059180 secs(本次回收的时间)] 2186589K(整个堆回收前的大小)->1343132K(整个堆回收后的大小)(3057292K(堆总大小)), 0.0059490 secs(回收时间)] [Times: user=0.00(用户耗时) sys=0.00(系统耗时), real=0.00 secs(实际耗时)]
  2. 老年代日志:2014-07-18T16:19:16.794+0800: 1630.821: [GC 1630.821: [DefNew: 1005567K->111679K(1005568K), 0.9152360 secs]1631.736: [Tenured: 2573912K->1340650K(2574068K), 1.8511050 secs] 3122548K->1340650K(3579636K), [Perm : 17882K->17882K(21248K)], 2.7854350 secs] [Times: user=2.57 sys=0.22, real=2.79 secs]

jstat

监控JVM的状态 官网地址

GC监控

# 每1秒对60805进程进行一次统计
jstat -gcutil  60805 1000
  1. YGC:年轻代垃圾回收次数
  2. YGCT:年轻代垃圾回收消耗时间
  3. FGC:老年代垃圾回收次数
  4. FGCT:老年代垃圾回收消耗时间
  5. GCT:垃圾回收消耗总时间

微信公众号搜索“八多编程”,第一时间了解文章推送

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值