Java内存模型



(图源: 深入理解JVM-内存模型(jmm)和GC)

区域名 英文名 访问权限 作用 备注
程序计数器 Program Counter Register 线程隔离 标记待取的下一条执行的指令 执行Native方法时为空; JVM规范中唯一不会发生OutOfMemoryError的区域
虚拟机栈 VM Stack 线程隔离 每个Java方法执行时创建,用于存储局部变量表,操作栈,动态链接,方法出口等信息 方法执行的内存模型
本地方法栈 Native Method Stack 线程隔离 Native方法执行时使用 JVM规范没有强制规定,如Hotspot将VM和Native两个方法栈合二为一
Java堆 Java Heap 线程共享 存放对象实例 更好的回收内存 vs 更快的分配内存
方法区 Method Area 线程共享 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据 JVM规范不强制要求做垃圾收集
运行时常量池 Runtime Constant Pool 线程共享 方法区的一部分
直接内存 Direct Memory - 堆外内存,通过堆的DirectByteBuffer访问 不是运行时数据区的一部分,但也可能OutOfMemoryError

对象的创建——new的时候发生了什么

讨论仅限于普通Java对象,不包括数组和Class对象。

  1. 常量池查找类的常量引用,如果没有先做类加载
  2. 分配内存,视堆内存是否是规整(由垃圾回收器是否具有压缩功能而定)而使用“指针碰撞”或“空闲列表”模式
  3. 内存空间初始化为零值,可能提前在线程创建时分配TLAB时做初始化
  4. 设置必要信息,如对象是哪个类的示例、元信息、GC分代年龄等
  5. 调用<init>方法

垃圾回收器总结

垃圾回收,针对的都是堆。

分代

  • 新生代:适合使用复制算法, 以下三个区一般占比为8:1:1

    • Eden 新对象诞生区
    • From Survivor 上一次GC的幸存者(见“GC种类-minor GC”)
    • To Survivor 本次待存放幸存者的区域
  • 老年代:存活时间较久的,大小较大的对象,因此使用标记-整理或标记-清除算法比较合适
  • 永久代:存放类信息和元数据等不太可能回收的信息。Java8中被元空间(Metaspace)代替,不再使用堆,而是物理内存。

分代的原因

  • 不同代的对象生命周期不同,可以针对性地使用不同的垃圾回收算法
  • 不同代可以分开进行回收

回收算法

名称 工作原理 优点 缺点
标记-清除 对可回收对对象做一轮标记,标记完成后统一回收被标记的对象 易于理解,内存利用率高 效率问题;内存碎片;分配大对象但无空间时提前GC
复制 内存均分两块,只使用其中一块。回收时将这一块存活对象全部复制到另一块 效率高 可用空间减少; 空间不够时需老年代分配担保
标记-整理 对可回收对对象做一轮标记,标记完成后将存活对象统一左移,清理掉边界外内存 内存利用率高 效率问题

标记-X算法适用于老年代,复制算法适用于新生代。

GC种类

  • Minor GC,只回收新生代,将Eden和From Survivor区的存活对象复制到To Survivor
  • Major GC,清理老年代。但因为伴随着新生代的对象生命周期升级到老年代,一般也可认为伴随着FullGC。
  • FullGC,整个堆的回收
  • Mixed GC,G1特有,可能会发生多次回收,可以参考关于G1 GC中Mixed GC的分析

垃圾回收器小结

垃圾回收器名称 特性 目前工作分代 回收算法 可否与Serial配合 可否与ParNew配合 可否与ParallelScavenge配合 可否与SerialOld配合 可否与ParallelOld配合 可否与CMS配合 可否与G1配合
Serial 单线程 新生代 复制 - - - Y N Y N/A
ParNew 多线程 新生代 复制 - - - N N Y N/A
ParallelScavenge 多线程, 更关注吞吐量可调节 新生代 复制 - - - N N Y N/A
SerialOld 单线程 老年代 标记-整理 - - - Y Y N N/A
ParallelOld 多线程 老年代 标记-整理 N N Y - - - N/A
CMS 多线程,并发收集,低停顿。但无法处理浮动垃圾,标记-清除会产生内存碎片较多 老年代 标记-清除 Y Y N Y - - N/A
G1 并行并发收集,追求可预测但回收时间,整体内存模型有所变化 新生代/老年代 整体是标记-整理,局部(两Region)复制 N N N N N N -

在本系列的上一篇文章关于GC(上):Apache的POI组件导致线上频繁FullGC问题排查及处理全过程中,减少FullGC的方式是使用G1代替CMS,计划在下一篇文章中对比CMS和G1的区别。

理解GC日志

只举比较简单的例子,具体各项的格式视情况分析,不同回收器也会有差异。

2019-11-22T10:28:32.177+0800: 60188.392: [GC (Allocation Failure) 2019-11-22T10:28:32.178+0800: 60188.392: [ParNew: 1750382K->2520K(1922432K), 0.0312604 secs] 1945718K->198045K(4019584K), 0.0315892 secs] [Times: user=0.09 sys=0.01, real=0.03 secs]

开始时间-(方括号[)-发生区域(ParNew,命名和GC回收器有关)-回收前大小-回收后大小-(方括号])-GC前堆已使用容量-GC后堆已使用容量大小-回收时间-使用时间详情(用户态时间-内核时间-墙上时钟时间)

注意这里没有包括“2019-11-22T10:28:32.177+0800: 60188.392: [GC (Allocation Failure)”这部分的分析。

可借鉴的编程模式

对象分配的并发控制

对象创建是很频繁的,在线程共享的堆中会遇到并发的问题。两种解决办法:

  1. 同步锁定:CAS+失败重试,确保原子性
  2. 堆中预先给每个线程划分一小块内存区域——本地线程分配缓冲(TLAB),TLAB使用完并分配新的TLAB时才做同步锁定。可看作1的优化。

CAS: Conmpare And Swap,用于实现多线程同步的原子指令。 将内存位置的内容与给定值进行比较,只有在相同的情况下,将该内存位置的内容修改为新的给定值。关于CAS可以参考:

Java中的CAS实现原理

CAS系列(3):CAS无锁自旋和同步锁线程切换使用场景对比

对象访问的定位方式

前提条件:通过上本地变量表的reference访问中的对象及它在方法区的对象类型数据(类信息)

主流的两种方式,这两种方式各有优点,可以看出方式2是方式1的优化,但并不是全面超越方式1,无法完全取代。

这里可以看到要权衡垃圾回收和访问速度两方面。

方式1: 直接指针访问实例数据



图源:深入理解JVM-内存模型(jmm)和GC

reference直接存放对象实例地址,只需要一次访问即可,执行效率较高。

方式2: 使用句柄池



图源:深入理解JVM-内存模型(jmm)和GC

reference中地址稳定,对象被移动时只需要改句柄池的地址。相对的,访问实例需要两次指针定位。

参考资料

  1. 周志明.著《深入理解JAVA虚拟机》
  2. 深入理解JVM-内存模型(jmm)和GC
  3. jvm的新生代、老年代、永久代关系
  4. JVM垃圾回收——新生代,老年代,永久代,Minor GC,Full GC

关于GC(中):Java垃圾回收相关基础知识的更多相关文章

  1. JVM垃圾回收的基础知识

    什么是垃圾? 没有任何引用指向的对象,就是垃圾 如何找到垃圾?(2 种方法) 过程:先找到正在使用的对象,然后把没有正在使用的对象进行回收 1.引用数-Reference-Count 被引用数为 0 ...

  2. JAVA面试题相关基础知识

        1.面向对象的特征有哪些方面 ①抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节 ...

  3. java OOP及相关基础知识汇总(转)

    OOP 对象有三个要素 behavior 接口是怎样的,有什么方法/field可以用? state 调用方法的时候,对象会有什么反应? 只有通过调用方法才能改变一个对象的state identity ...

  4. Java GC(垃圾回收)机制知识总结

    目录 Java GC系列 Java关键术语 Java HotSpot 虚拟机 JVM体系结构 Java堆内存 启动Java垃圾回收 Java垃圾回收过程 垃圾回收中实例的终结 对象什么时候符合垃圾回收 ...

  5. [译]Java垃圾回收是如何工作的

    说明:这篇文章来翻译来自于Javapapers 的How Java Garbage Collection Works 这部分教程是为了理解Java垃圾回收的基础以及它是如何工作的.这是垃圾回收系列教程 ...

  6. [译]Java 垃圾回收介绍

    说明:这篇文章来翻译来自于Javapapers 的Java Garbage Collection Introduction 在Java中,对象内存空间的分配与回收是由JVM中的垃圾回收进程自动完成的. ...

  7. 细述 Java垃圾回收机制→Types of Java Garbage Collectors

    细述 Java垃圾回收机制→Types of Java Garbage Collectors 转自:https://segmentfault.com/a/1190000006214497 本文非原创, ...

  8. Java基础知识强化83:System类之gc()方法(垃圾回收)以及和finalize()区别

    1. System概述: System类包含一些有用的类字段和方法.它不能被实例化. 2. gc()方法:垃圾回收器 public static void gc()       调用gc方法暗示着Ja ...

  9. Java GC系列(1):Java垃圾回收简介

    本文由 ImportNew - 好好先生 翻译自 javapapers. Java的内存分配与回收全部由JVM垃圾回收进程自动完成.与C语言不同,Java开发者不需要自己编写代码实现垃圾回收.这是Ja ...

随机推荐

  1. Linux必备工具与软件包

    yum -y update(所有都升级和改变) 升级所有包,系统版本和内核,改变软件设置和系统设置 ----------------------------------------------- yu ...

  2. 玩转OneNET物联网平台之MQTT服务① —— OneNetMqtt全方位调试

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  3. .NETCore下CI/CD之自动化测试

    前言 为了呼应<中国.NET开发者峰会2019上海站>,作为演讲嘉宾,我希望和各位同行建立更多的互动,为此,我特地将部分演讲内容,整理成文章先行发布.本文从零开始,一步一步的引导,从安装J ...

  4. 保存为txt

    打开对话框保存为txt #region this.dDownTable = (DataTable)(this.dg1.DataContext); ) { string fName = string.E ...

  5. 用Python将处理数据得到的csv文件分类(按顺序)保存

    用Python中的os和numpy库对文件夹及处理数据后得到的文件进行分类保存: import numpy as np import os for m in range(699,0,-35): cur ...

  6. kmp算法,求重复字符串

    public class Demo { public static void main(String[] args) { String s1 = "ADBCFHABESCACDABCDABC ...

  7. kali更新源地址更改

    问题: Hit:1 http://mirrors.ustc.edu.cn/kali kali-rolling InReleaseIgn:2 http://mirrors.ustc.edu.cn/kal ...

  8. Knative 实战:如何在 Knative 中配置自定义域名及路由规则

    作者 | 元毅 阿里云智能事业群高级开发工程师 当前 Knative 中默认支持是基于域名的转发,可以通过域名模板配置后缀,但目前对于用户来说并不能指定全域名设置.另外一个问题就是基于 Path 和 ...

  9. Java多线程编程(三)线程间通信

    线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...

  10. 基于Java的开源爬虫框架WebCollector的使用

    一.WebCollector介绍 WebCollector是一个无须配置.便于二次开发的JAVA爬虫框架(内核),它提供精简的的API,只需少量代码即可实现一个功能强大的爬虫. WebCollecto ...