玩转Java虚拟机(十)

打卡学习JVM,第十天

本人学习过程中所整理的代码,源码地址

内存分配

在这里插入图片描述

- 堆上分配

  • 大多数情况在Eden上分配,偶尔会直接在old上分配
  • 细节取决于GC的实现

- 栈上分配

  • 原子类型的局部变量

内存回收

GC要做的就是将那些dead的对象所占用的内存回收掉

  • Hotspot认为没有引用的对象是dead的
  • Hotspot将引用分为四种:Strong(默认通过Object obj = new Object()这种方式赋值的引用)和Soft、Weak、Phantom(都继承了Reference)

在Full GC时会对Reference类型的引用进行特殊处理

  • Soft:内存不够时或者长期不用时一定会被GC
  • Weak:一定会被GC,当被mark为dead,会在ReferenceQueue中通知
  • Phantom:本来就没引用,当从JVM堆中释放时会通知

GC的时机

在分代模型的基础上,GC从时机上 分为两种:MinorGC和Full GC

- Minor GC

  • 触发时机:新对象生产时,Eden空间满了
  • 理论上Eden区大多数对象会在Minor GC回收,复制算法的执行效率会很高,Minor GC时间比较短

- Full GC

  • 对整个JVM进行整理,包括新生代,老年代,永久代
  • 主要触发时机:1)老年代满了 2)永久代满了 3)System.gc()
  • 效率很低,尽量减少Full GC

GC要做的就是将那些dead的对象所占用的内存回收掉

  • Hotspot认为没有引用的对象是dead的
  • Hotspot将引用分为四种:Strong(默认通过Object obj = new Object()这种方式赋值的引用)和Soft、Weak、Phantom(都继承了Reference)

在Full GC时会对Reference类型的引用进行特殊处理

  • Soft:内存不够时或者长期不用时一定会被GC
  • Weak:一定会被GC,当被mark为dead,会在ReferenceQueue中通知
  • Phantom:本来就没引用,当从JVM堆中释放时会通知

垃圾回收器在这里插入图片描述

  • 分代模型:GC的宏观愿景
  • 垃圾回收器:GC的具体实现
  • Hotspot JVM提供多种垃圾回收器,需要根据具体应用的需要采用不同的垃圾回收器
  • 没有万能的垃圾回收器

- 垃圾回收器的“并行”和“并发”

  • 并行(Parallel):指多个收集器的线程同时工作,但是用户线程处于等待状态
  • 并发(Concurrent):指收集器在工作的同时可以允许用户线程工作

并发不代表解决了GC停顿的问题,在关键的步骤还是要停顿。比如在收集器标记垃圾的时候。但在清理垃圾的时候,用户线程可以和GC线程并发执行

- Serial收集器

  • 最早的收集器单线程收集器,没有多线程切换的额外开销,简单实用,收集时会暂停所有工作线程(Stop The World,STW),虚拟机运行在Client模式时的默认新生代收集器
  • 在新生代采用复制算法,在老年代采用标记-整理算法
  • Hotspot Client模式缺省的收集器
    在这里插入图片描述

- ParNew收集器

  • Serial的多线程版本
  • 对应的这种收集器是虚拟机运行在Server模式的默认新生代收集器,在单CPU的环境中性能和Serial收集器差不多
  • 使用复制算法
  • 可以通过-XX:ParallelGCThreads来控制GC线程数的多少,需要结合具体CPU的个数
  • Server模式下新生代的缺省收集器
    在这里插入图片描述

- Parallel Scavenge收集器

Parallel Scavenge 收集器是以吞吐量最大化(即GC时间占总运行时间最小)为目标的收集器实现,它允许较长时间的STW换取总吞吐量最大化

- Serial Old收集器

Serial Old是单线程收集器,使用标记-整理算法,是老年代的收集器

- Parallel Old收集器

老年代版本吞吐量优先收集器,使用多线程和标记-整理算法

  • Parallel Scavenge在老年代的实现
  • 在JVM1.6才出现
  • 采用多线程,标记-整理算法
  • 更注重吞吐量
  • Parallel Scavenge + Parallel Old = 高吞吐量,但GC停顿可能不理想在这里插入图片描述

    - CMS(Concurrent Mark Sweep)收集器

CMS是一种以最短停顿时间为目标的收集器,使用CMS并不能达到GC效率最高(总体GC时间最小),但它能尽可能降低GC时服务的停顿时间,CMS收集器使用的是标记-清除算法

  • 追求最短停顿时间,非常适合Web应用
  • 只针对老年代,一般结合ParNew使用
  • GC线程与用户线程尽量并发工作
  • 标记-清除算法
  • 多CPU环境下才有意义
  • 使用-XX:+UseConcMarkSweepGC打开
  • 缺点:以牺牲CPU资源的代价来减少用户线程的停顿;CMS并发清理过程中,由于用户线程还在允许,因此需要预留一部分空间给用户线程;碎片化问题
    在这里插入图片描述

Java内存泄漏的经典原因

- 对象定义在错误的范围(Wrong Scope)

  • 如果Foo实例对象的生命较长,会导致临时性内存泄漏(这里的names变量其实只有临时作用)
class Foo {
	private String[] names;
	public void doIt(int length) {
		if (names == null || names.length < length) {
			names = new String[length];
			populate(names);
			System.out.println(names);
		}
	}
}
  • JVM喜欢生命周期短的对象,这样做已经足够高效
class Foo {
	public void doIt(int length) {
		String[] names = new String[length];
		populate(names);
		System.out.println(names);
	}
}

- 异常(Exception)处理不当

Connection conn = DriverManager.getConnection(url,name,pwd);
try {
    String sql = "do a query sql";
    PreparedStatement stmt = conn.prepareStatement(sql);
    ResultSet rs = stmt.executeQuery();
    while (rs.next()){
        doSomeStuff();
    }
    rs.close();
    conn.close();
 }catch (Exception e){
 }

如果doSomeStuff()方法抛出异常,rs.close()和conn.close()不会被调用,因此会导致内存泄漏和数据库连接泄漏

- 集合数据管理不当

  • 当使用基于数组的数据结构(ArrayList,HashMap等)时,尽量减少resize
  • 如果一个List只需要顺序访问,不需要随机访问,用LinkedList代替ArrayList,前者本质是链表,不需要resize

GC垃圾收集器的JVM参数定义

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

发布了10 篇原创文章 · 获赞 20 · 访问量 956

猜你喜欢

转载自blog.csdn.net/qq_41982594/article/details/105045828