way-to-architect
  • 前言
  • Java
    • Java关键字
      • Java中四种修饰符的限制范围
      • static和final
    • 容器
      • 容器概述
        • 容器:综述
        • Iterator原理及实现
        • fast-fail机制
        • 比较器Comparator
        • Collections工具类
      • List
        • List综述
        • ArrayList原理分析
        • ArrayList在循环过程中删除元素的问题
        • 常用的小技巧
        • CopyOnWrite
      • Set
        • Set综述
        • HashSet
        • LinkedHashSet
        • TreeSet
      • Queue
        • Queue综述
        • ArrayBlockingQueue实现原理
        • LinkedBlockingQueue实现原理
        • 高性能无锁队列Disruptor
      • Map
        • Map综述
        • HashMap
          • HashMap实现原理
          • HashMap中的位运算
          • HashMap其他问题
        • LinkedHashMap
        • TreeMap
        • ConcurrentHashMap
          • ConcurrentHashMap实现原理JDK1.7
          • ConcurrentHashMap实现原理JDK1.8
        • ConcurrentSkipListMap
        • Map中key和value的null的问题
    • 线程
      • 线程
        • 创建线程
        • 线程状态及切换
        • 线程中断的理解
        • 几种方法的解释
        • 用户线程与守护线程
        • 线程组ThreadGroup
      • 线程池
        • 线程池工作原理及创建
        • Executor
        • 如何确保同一属性的任务被同一线程执行
      • ThreadLocal
        • ThreadLocal原理
        • ThreadLocal之父子线程传值
        • InheritableThreadLocal
      • 同步与锁
        • 线程安全与锁优化
        • synchronize关键字
        • Lock
          • 队列同步器
            • 同步状态的获取与释放
            • 使用方式
            • 示例:Mutex
            • 示例:TwinsLock
          • 重入锁和读写锁
          • LockSupport
          • Condition
          • 并发工具类
        • CAS
          • CAS的理解
          • Java中原子操作类
        • 3个经典同步问题
      • fork/join的理解
    • I/O
      • I/O概述
        • 磁盘I/O与网络I/O
        • 主要接口
        • 输入流和输出流的使用示例
        • InputStream的重复读
        • BufferdxxxxStream
        • Serailizable
        • File常用方法
        • Files和Path
        • RandomAccessFile
        • 通过零拷贝实现有效数据传输
        • 正确地处理文件
      • NIO基础
      • NIO2
      • Netty
        • Java I/O的演进之路
        • 为什么是Netty
        • 更多
      • I/O调优
    • 异常
      • 异常体系及为什么要有这种异常设计
      • 多catch的执行情况
      • try catch finally 与reture
      • 异常处理的误区
      • Preconditions:方法入参校验工具
    • 枚举
      • 常见用法
      • 枚举类在序列化中的问题
    • 注解
      • 概述
      • Spring中的组合注解的条件注解
      • 常用注解
        • JSR-330标准注解
    • 反射
      • 概述
      • 内部类的反射
      • 反射中需要注意的地方
    • 流程控制
      • switch case without break
      • Java: for(;;) vs. while(true)
    • JVM
      • JVM内存结构
      • Java内存模型
      • 垃圾收集器和内存分配策略
      • 四种引用类型区别及何时回收
      • 类文件结构
      • 类初始化顺序
      • 类加载机制
      • 虚拟机执行引擎
      • 逃逸分析
      • JVM常用配置
      • GC日志分析
      • Java8 JVM 参数解读
      • 垃圾收集器和内存分配策略
    • 面向对象
      • Object类中的方法
      • Class类中的方法
      • 值传递还是引用传递?
      • 接口和抽象类的区别
      • 深拷贝和浅拷贝
      • Integer.parseInt()与Interger.valueof()
      • hashCode()与equal()
      • String
        • String池化及intern()方法的作用
        • 关于字符串
    • 序列化
      • Java序列化的方式有哪些?
    • 新特性
      • 流 Stream
        • Stream是什么
        • Stream API详解
        • Stream进阶
        • 流编程
        • 其他事项
      • lambda表达式
      • 默认方法(Default Methods)
      • @FunctionalInterface注解
    • SPI
      • 理解SPI
    • 字节码
      • javaagent
      • 字节码操纵
      • 如何查看类编译后的字节码指令
      • 字节码指令有哪些
  • Python
    • 异常处理
  • Go
  • 数据结构与算法
    • 数据结构
      • 概述
        • 线性表
        • 栈
        • 队列
        • 串
        • 树
        • 图
      • Java的一些实现
      • 红黑树
      • 双缓冲队列
      • 跳表SkipList
    • 算法
      • 概述
      • 常见算法
        • 基本排序
        • 高级排序
        • 动态规划
  • 框架或工具
    • Spring
      • Spring基础
        • Spring整体架构
        • 什么是IoC
        • Ioc容器的基本实现
        • Spring的MainClass
          • Spring的BeanFactory
          • Spring的Register
          • Spring的Resource和ResourceLoader
          • Spring的PropertySource
          • Spring的PropertyResolver
          • Spring的PropertyEditor
          • Spring的Convert
          • Spring的BeanDefinition
          • Spring的BeanDefinitionReader
          • Spring的BeanDefiniton其他Reader
          • Spring的BeanDefinition其他Reader2
          • Spring的Aware
          • Spring的BeanFctoryPostProcessor
          • Spring的BeanPostProcessor
          • Spring的Listener
        • Xml格式的应用启动
          • Xml格式的应用启动2
          • Xml格式的应用启动3
          • Xml格式的应用启动4
          • Xml格式的应用启动5
          • Xml格式的应用启动6
          • Xml格式的应用启动7
        • Spring中的设计模式
        • 什么是AOP
        • Spring中AOP的实现
      • Spring应用
        • Spring的事务控制
        • @Transactional注解在什么情况下会失效
        • 如何在数据库事务提交成功后进行异步操作
        • Spring中定时任务的原理
    • SpringMVC
      • Controller是如何将参数和前端传来的数据一一对应的
      • 请求处理流程
    • Zookeeper
      • Zookeeper是什么
      • Zookeeper能干啥
    • Shiro
    • druid
    • Netty
    • Consul
      • Consul是什么
    • etcd
    • confd
    • Akka
      • Actor模型是什么
  • 数据库
    • 基本概念
    • MySQL
      • 基本配置
      • MySQL数据类型
      • MySQL存储引擎
      • MySQL事务
        • MySQL事务概念
      • MySQL索引
        • MySQL中的索引类型
        • B-Tree/B+Tree概述
        • 为什么使用B+Tree
        • MySQL中的B+Tree索引
        • MySQL高性能索引策略
      • MySQL查询
        • MySQL查询过程
        • MySQL查询性能优化
        • 使用EXPLAIN
      • MySQL锁
        • MySQL中锁概述
        • InnoDB的并发控制
        • MySQL乐观锁
      • MySQL分库分表
        • 分库/分表
        • 跨库JOIN
        • 跨库分页
        • 分库分表后的平滑扩容
        • 分区表
        • 分布式ID生成方法
      • MySQL实战
        • 在线表结构变更
        • MySQL优化规则
        • MySQL问题排查
        • 常见查询场景
    • Redis
    • Hbase
    • OpenTSDB
    • rrd
    • MongoDB
    • 连接池
  • 系统设计
    • 一致性Hash算法
    • 限流
      • 限流是什么
      • 限流算法
      • 应用内限流
      • 分布式限流
      • 接入层限流
        • ngx_http_limit_conn_module
        • ngx_http_limit_req_module
        • lua_resty_limit-tarffic
      • 节流
    • 降级
      • 降级详解
      • 人工降级开关的实现
      • 自动降级的实现:Hystrix
    • 负载均衡
      • 概述
      • 互联网架构下的负载均衡
      • Nginx负载均衡(七层)
      • Nginx负载均衡(四层)
      • Nginx动态配置
    • 超时与重试机制
      • 什么地方要超时与重试
      • 代理层超时与重试
      • Web容器超时
      • 中间件客户端超时与重试
      • 数据库超时
      • NoSQL客户端超时设置
      • 业务超时
      • 前端请求超时
    • 网关
    • CAP
      • 什么是CAP
      • CAP理解
    • 生产者-消费者模型
      • 使用notify/wait方式
      • 使用await/signal实现
      • 使用阻塞队列实现
      • 使用信号量实现
      • 使用管道流实现
      • 无锁队列Disruptor
      • 双缓冲队列
    • 缓存
      • 缓存概述
      • 数据库缓存
      • 应用缓存
      • 前端缓存
      • 本地缓存
    • 秒杀
    • LRU
  • 版本控制
    • Git
      • Git常用命令
      • 场景命令
    • Svn
  • 计算机操作系统
    • Linux
      • Linux中重要概念
      • 常用命令
      • 查看日志
      • 权限管理
      • 登录或传输
      • 防火墙
      • 配置ssh免密
      • 进程
      • 防火墙
    • Mac
    • 计算机基础
      • 进制
      • Java中的位运算
      • 计算机存储系统结构
  • 网络
    • TCP三次握手和四次挥手
    • 网络术语
      • 网关、路由器、交换机、IP等
      • VLAN
      • LAN
  • 设计模式
    • 设计模式概述
    • 创建型
      • 单例模式
      • 工厂模式
      • 建造者模式
      • 原型模式
      • 享元模式
    • 行为型
      • 观察者模式
      • 策略模式
      • 模板模式
      • 责任链模式
      • 命令模式
      • 外观模式
      • 迭代器模式
      • 中介者模式
        • 中介模式续
      • 状态模式
        • 状态模式实例
        • 状态模式思考
      • 访问者模式
        • 访问者实例1
        • 访问者模式续
    • 结构型
      • 组合模式
        • 组合模式续
      • 装饰模式
        • 装饰模式续
      • 代理模式
      • 备忘录模式
      • 桥接模式
        • 桥接模式实例一
  • 构建工具
    • Maven
      • 常用命令
      • Maven生命周期
      • Maven中的变量和属性
      • 不同环境的如何配置不同的变量
      • 常用插件及配置
      • 其他问题
      • dependencies与dependencyManagement的区别
    • Gradle
  • 大数据
    • Hadoop
    • Storm
    • Spark
  • 服务器
    • Tomcat
      • server.xml配置详解
      • 线程池和连接数配置
      • Maven远程部署
      • 一些小技巧
      • Tomcat类加载机制分析
      • Tomcat的日志
      • Tomcat架构
        • 概述
        • Server 的启动流程
        • 请求处理流程
    • Nginx
      • 常用命令
      • 基本配置
      • Lua
    • Tengine
  • 中间件
    • 任务调度
      • 为什么需要任务调度
    • 消息队列
      • 为什么需要消息队列
      • 消息队列关键点
      • 消息中间件需要解决的问题
      • 不同消息队列产品对比
      • RocketMQ
        • 快速入门
        • 整体架构
        • 部署方式
          • Broker部署方案
        • 客户端使用
          • 客户端使用指南
          • 快速开始
          • 简单示例
          • 有序消息示例
          • 广播消息示例
          • 定时消息示例
          • 批量消息示例
          • 过滤消息示例
          • 日志输出配置示例
        • 关键点实现
          • 顺序消息的实现
        • 最佳实践
          • Broker的最佳实践
          • 生产者最佳实践
            • 生产者最佳实践续
          • 消费者最佳实践
            • 消费者最佳实践续
          • 名称服务最佳实践
          • JVM/kernel配置的最佳实践
          • 新特性 Filter Server
          • 其他事项
      • RabbitMQ
      • Kafka
    • 分布式事务
      • 什么是分布式事务
      • 解决方案
    • 服务治理
      • RPC概念
      • RPC最简实现
      • 为什么需要服务治理
      • Dubbo
        • Dubbo整体架构
      • Java RMI
    • 分布式锁
      • 如何设计分布式锁
        • 基于zookeeper
        • 基于Redis
    • 注册中心
      • 注册中心的职责
      • 不同注册中心的比较
    • 配置中心
      • 概述
      • 配置中心的实现与选型
  • Web开发
    • Http请求类型及区别
    • 常见的content-type
    • 如何处理跨域
    • Restful最佳实践
    • HTTP状态码
    • Http下载原理
  • 测试
    • 压测:apache bench
    • 压测:Jmeter
Powered by GitBook
On this page
  • 判断对象的状态
  • 二、垃圾收集算法
  • 垃圾收集器
  • 内存分配与回收策略
  1. Java
  2. JVM

垃圾收集器和内存分配策略

判断对象的状态

在垃圾收集器对堆进行回收前,要先确定对象的状态:是还存活着,还是已经死去。

  • 引用计数算法 做法:给对象一个引用计数器,每当有对象引用它时,计数器就加1;当引用失效时,计数器就减1;任何时刻计数器为0的对象就是不可能再被使用的。 引用计数算法实现简单,判定效率高,在大部分情况下都是一个不错的算法。但主流的Java虚拟机里都没有选用该算法来管理内存:它很难解决对象之间的相互循环引用的问题。

  • 可达性分析算法 该算法的基本思想是:通过一系列的称为GC Roots的对象作为起始点,从这些节点向下搜索,搜索所走过的路径称为引用链(Reference Chain)。当一个对象到GC Roots没有任何引用链相连(从GC Roots到这个对象不可达),则证明该对象不可用。 在Java中,可作为GC Roots的对象包括下面几种:

    • 虚拟机栈(栈帧中的本地变量表)中引用的对象(即当前正在运行的代码用到的对象)

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

    • 方法区中常量引用的对象

    • 本地方法栈中JNI(一般说的Native方法)引用的对象

  • 方法区的回收 方法区中垃圾回收的性价比较低(很少可以被回收)。HotSpot虚拟机中方法区使用永久代来实现的。 永久代的垃圾回收主要包括两部分:废弃常量和无用的类。 对于废弃常量的判断与Java堆中的对象判断类似。 而类则要同时满足下面的3个条件才能算是无用的类: a.该类的所有实例都已经被回收,也就是堆中不存在该类的任何实例 b.加载该类的ClassLoader已经被回收 c.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。 满足上面3个条件后,并不一定会回收,只是可以回收了。是否对类进行回收,HotSpot提供了-Xnoclassgc参数进行控制。

二、垃圾收集算法

  • 标记-清除算法 该算法分为两个阶段:标记和清除,首先标记出所有需要回收的对象,在标记完成之后,统一回收所有被标记的对象。 不足之处:一是效率问题,标记和清除两个过程的效率都不高;二是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后再程序中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作。

  • 复制算法 复制算法将内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完,就将还存活的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。这样,每次回收只是针对整个半区,且不会产生内存碎片问题。 该算法实现简单,运行高效,但代价是将内存缩小了一半。 现在商业虚拟机均采用该算法对新生代进行回收,但并不按照1:1的比例划分内存。而是将内存分为一块较大的Eden空间和两块较小的Survivor空间。每次使用Eden空间和其中一块Survivor空间。当回收时,将Eden空间和Survivor空间中还存活的对象复制到另一块Survivor空间中,最后清理掉Eden空间和之前用过的Survivor空间。(适用于对象存活率较低的场景) HotSpot中默认Eden:Survivor:Survivor = 8:1:1。 如果在回收时,有大于10%时的对象都存活,即预留的Survivor空间不够用,就要依赖其他内存(老年代)进行分配担保:直接进入老年代。

  • 标记-整理算法 针对老年代的一种算法。该算法在标记阶段与标记-清除算法一致,但后续并不对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

  • 分代收集算法 顾名思义,根据对象的存活周期的不同将内存划分为几块,一般是将堆分为新生代和老年代,然后根据各个年代的特点采用最适当的收集算法。 在新生代,每次垃圾回收时都发现有大批对象死去,只有少量存活,就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。 在老年代,由于对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清除或者标记-整理算法进行回收。

垃圾收集器

收集算法是内存回收的方法论,而垃圾收集器则是内存回收的具体实现。没有万能的收集器,没有最好的收集器,只是对具体应用选择最合适的收集器。

  • Serial收集器

    • 单线程的新生代收集器,在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。

    • 采用复制算法

    • 简单而高效(与其他收集器的单线程比),对于限定的单个CPU的环境下,没有线程交互的开销,专心做垃圾回收操作。

    • 是虚拟机运行在Client模式下的默认新生代收集器。

  • ParNew收集器

    • 其实就是Serial收集器的多线程版本,其他与Serial收集器完全一样(比如控制参数、收集算法、Stop The World、对象分配规则、回收策略) 。默认开启的收集线程与CPU数量相同,在CPU非常多的环境下,可使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。

    • 是很多运行在Server模式下的虚拟机中首选的新生代收集器:除了Serial收集器,只有它能与CMS收集器配合工作。它也是使用CMS收集器时默认的新生代收集器。

  • Parallel Scavenge收集器

    • 新生代收集器,使用复制算法,并行的多线程收集器。

    • 该收集器的目标是达到一个可控制的吞吐量(CPU用于运行用户代码的时间与CPU总消耗时间的比值),吞吐量=运行用户代码时间/(运行用户代码时间 + 垃圾收集时间),被称为吞吐量优先的收集器。

    • 高吞吐量可以高效率地利用CPU时间,尽快完成程序的运算任务,适合在后台运算而不需要太多交互的任务。

    • 设定两个参数来控制吞吐量:最大垃圾收集停顿时间-XX:MaxGCPauseMills和吞吐量大小-XX:GCTimeRatio。

    • 自适应调节策略:通过参数-XX:+UseAdaptiveSizePolicy,开启之后,就不需要手工指定新生代大小、Eden与Survivor区的比例、晋升老年代对象的大小等细节参数,虚拟机会根据当前系统运行情况进行GC自适应调节。

  • Serial Old收集器

    • Serial收集器的老年代版本,单线程,使用标记-整理算法。

    • 主要意义是在给定Client模式下的虚拟机使用。在Server模式下,有两种用途:①在JDK1.5以及之前的版本中与Parallel Scavenge收集器搭配使用;②作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。

  • Parallel Old收集器

    • Parallel Scavenge收集器的老年代版本,多线程,使用标记-整理算法。

    • JDK1.6中才开始提供该收集器。在此之前,新生代如果选择了Parallel Scavenge收集器,老年代只能选择Serial Old收集器。Parallel Old收集器的出现,使得“吞吐量优先收集器”有了比较名副其实的组合:Parallel Scavenge + Parallel Old。

  • CMS收集器

    • 老年代收集器

    • CMS(Concurrent Mark Sweep)收集器是一种获取最短回收停顿时间为目标的收集器。优点是并发、低停顿。在互联网网站或者B/S系统的服务端上的Java应用,注重服务的响应时间,希望系统停顿时间最短,以带给用户较好的体验,CMS收集器非常适合这类应用的需求。

    • 基于标记-清除算法,运作过程分为4个步骤: a.初始标记:需要Stop The World,标记GC Roots能直接关联到的对象,速度很快。 b.并发标记:与用户程序一起运行,进行GC Roots Tracing的过程。 c.重新标记:需要Stop The World,修正并发标记阶段因用户程序继续运作而导致标记产生变化的那一部分对象的标记记录。该阶段的停顿时间比初始阶段稍长,但远小于并发标记的停顿时间。 d.并发清除:与用户程序一起运行,进行清除工作。

    • CMS远达不到完美的程度,它有4个明显的缺点: a.对CPU非常敏感:因占用CPU资源导致应用程序变慢,总吞吐量降低。CMS默认的垃圾收集线程数为(CPU数量 + 3)/4。当CPU在4个以上时,垃圾回收线程会至少占用25%的CPU资源,并会随着CPU数量增加而下降。当CPU不足4个时,CMS对用户程序的影响可能就非常大。 b.CMS无法处理浮动垃圾。由于CMS并发清理阶段用户线程还在运行,可能会出现新的垃圾,CMS无法在本次收集时处理它们,只能等下一次GC再清理。这部分垃圾就是浮动垃圾。 c.由于并发收集,在收集时还要预留内存空间供用户程序使用,因此CMS收集器无法像其他收集器一样等到老年代几乎被完全填充时再进行收集。在JDK1.5的默认设置下,当老年代使用了68%的时候,就会进行垃圾收集。如果应用中老年代增长不快,可适当提高该值。JDK1.6将该值提升到92%。但不能设置的太高:如果在CMS运行期间预留的内存空间无法满足程序需要,就会出现Concurrent Mode Failure,这时虚拟机将会启动后备预案:临时启用Serial Old收集器重新进行老年代的垃圾收集,导致停顿时间很长。可以通过参数-XX:CMSInitiatingOccupancyFraction设置该值。 d.基于标记-清除算法,容易造成内存碎片,往往出现老年代还有很大空间剩余,但无法找到足够大的连续空间来分配当前对象,不得不出发一次Full GC。可以通过参数-XX:UseCMSCompactAtFullCollection(默认开启),用于在CMS顶不住要进行一次FullGC时开启内存碎片整理过程(该过程无法并发,需要停顿)。还可以使用参数-XX:CMSFullGCsBeforeCompaction设定执行多少次不压缩的FullGC后,跟着来一次带压缩的(默认为0,即每次进入FullGC时都进行碎片压缩)。

  • G1收集器

    • Garbage-First是当今收集器技术发展的最前沿成果之一。

    • G1是一款面向服务器端应用的垃圾收集器,相比于CMS,优点是:

      ①并行与并发:缩短Stop The World的时间;

      ②分代收集:将堆划分为多个大小相等的独立区域Region,虽然还保留新生代和老年代的概念,但新生代和老年代不再是物理隔离了,他们都是一部分Region的集合(不需要连续);

      ③空间整合:从整体上看基于标记-整理算法,从局部(两个Region)看是基于复制算法,不会产生内存碎片;

      ④可预测的停顿:可以指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。

内存分配与回收策略

对象的内存分配,就是在堆上分配,对象主要分配在新生代的Eden区域,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配。少数情况也可能会直接分配在老年代中。 分配的规则不是百分百固定的。其细节取决于当前使用的是哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数的配置。 最普遍的内存分配规则有以下几点:

  • 对象优先在Eden分配

  • 大对象直接进入老年代 通过设置参数-XX:PretenureSizeThreshold参数,令大于该设置值的对象直接在老年代分配。这样做的目的是为了避免在Eden区和两个Suvivor区之间发生大量的内存复制。

  • 长期存活的对象将进入老年代 虚拟机给每个对象定义一个对象年龄计数器(在对象头中)。如果对象在Eden出生并且经过第一次Monior GC后仍然存活,并且被Survivor容纳的话,就将该对象的年龄设为1。对象在Survivor区中每熬过依次Monior GC,年龄就增加1。当该对象的年龄增加到一定程度(默认为15),就会进入老年代。可以通过参数-XX:MaxTenuringThreshold设置年龄阈值。

    • 动态对象年龄判定

      如果在Survivor区间中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄>=该年龄的对象就可以直接进入老年代,无需等到参数-XX:MaxTenuringThreshold设置的年龄。

  • 空间分配担保 在发生Monior GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果大于,则Monior GC可以确保是安全的。如果小于,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败,如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,就尝试进行一次Monior GC(有风险),如果小于或者HandlePromotionFailure设置不允许冒险,则此时改为进行一次Full GC。

概念补充

内容摘抄自《深入理解Java虚拟机》

PreviousJava内存模型Next四种引用类型区别及何时回收

Last updated 6 years ago

空间分配担保.png

Minor GC:清理年轻代(包括 Eden 和 Survivor 区域),所有的 Minor GC 都会触发stop-the-world。 Major GC:清理老年代。 Full GC:清理整个堆空间—包括年轻代和老年代。 参考自:

Minor GC、Major GC和Full GC之间的区别