jvm内存区域&工具&笔记

转摘 https://www.cnblogs.com/czwbig/p/11127124.html

https://github.com/hollischuang/toBeTopJavaer

java8来自网络

java8之前

image.png

1、程序计数器

  • 程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
  • 为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。
  • 如果线程正在执行的是一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。

2、Java虚拟机栈(JVM Stack)

  • 与程序计数器一样,Java 虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。
  • 每个方法在执行的同时都会创建一个栈帧(Stack Frame,是方法运行时的基础数据结构)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。


    image.png

2.1 局部变量表

  • 局部变量表是存放方法参数和局部变量的区域。 局部变量没有准备阶段, 必须显式初始化。如果是非静态方法,则在 index[0] 位置上存储的是方法所属对象的实例引用,一个引用变量占 4 个字节,随后存储的是参数和局部变量。字节码指令中的 STORE 指令就是将操作栈中计算完成的局部变呈写回局部变量表的存储空间内。
  • 虚拟机栈规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常;如果虚拟机栈可以动态扩展(当前大部分的 Java 虚拟机都可动态扩展),如果扩展时无法申请到足够的内存,就会抛出 OutOfMemoryError 异常。

2.2 操作栈

  • 操作栈是个初始状态为空的桶式结构栈。在方法执行过程中, 会有各种指令往栈中写入和提取信息。JVM 的执行引擎是基于栈的执行引擎, 其中的栈指的就是操作栈。字节码指令集的定义都是基于栈类型的,栈的深度在方法元信息的 stack 属性中。
  • i++ 和 ++i 的区别:
    -- i++:从局部变量表取出 i 并压入操作栈(load memory),然后对局部变量表中的 i 自增 1(add&store memory),将操作栈栈顶值取出使用,如此线程从操作栈读到的是自增之前的值。
    -- ++i:先对局部变量表的 i 自增 1(load memory&add&store memory),然后取出并压入操作栈(load memory),再将操作栈栈顶值取出使用,线程从操作栈读到的是自增之后的值。

3、本地方法栈

  • 本地方法栈(Native Method Stack)与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。Sun HotSpot 虚拟机直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出 StackOverflowError 和 OutOfMemoryError 异常。

4、Java堆

  • 对于大多数应用来说,Java 堆(Java Heap)是 Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
  • 堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”(Garbage Collected Heap)。从内存回收的角度来看,由于现在收集器基本都采用分代收集算法,所以 Java 堆中还可以细分为:新生代和老年代;再细致一点的有 Eden 空间、From Survivor 空间、To Survivor 空间等。从内存分配的角度来看,线程共享的 Java 堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)。
  • Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,当前主流的虚拟机都是按照可扩展来实现的(通过 -Xmx 和 -Xms 控制)。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出 OutOfMemoryError 异常。

5、方法区

  • 方法区(Method Area)与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然
    Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。
JDK8 之前,Hotspot 中方法区的实现是永久代(Perm),JDK8 开始使用元空间(Metaspace),以前永久代所有内容的字符串常量移至堆内存,其他内容移至元空间,元空间直接在本地内存分配。
为什么要使用元空间取代永久代的实现?
  • 字符串存在永久代中,容易出现性能问题和内存溢出。
  • 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
  • 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
  • 将 HotSpot 与 JRockit 合二为一。

5.1 运行时常量池

  • 运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

6、 直接内存

  • 直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域。
  • 在 JDK 1.4 中新加入了 NIO,引入了一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。
public void method1(){
  Object obj = new Object();
}
  • 生成了两部分的内存区域
  • obj这个引用变量,因为是方法内的变量,放到jvm stack里面
  • obj的实例对象,放到Heap里面
  • 上述的new语句一共消耗12个bytes,jvm规定引用占用4个bytes,而空对象是8个bytes,
  • 方法结束后,对应的stack中的变量引用马上被回收,heap中 对象要等到gc来回收

Java内存模型

  • Java 内存模型的主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量(线程共享的变量)存储到内存和从内存中取出变量这样底层细节。

  • Java内存模型中规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。

  • 这里的工作内存是 JMM 的一个抽象概念,也叫本地内存,其存储了该线程以读 / 写共享变量的副本。

  • 不同线程之间无法直接访问对方工作内存中的变量,线程间的通信一般有两种方式进行,一是通过消息传递,二是共享内存。Java 线程间的通信采用的是共享内存方式,线程、主内存和工作内存的交互关系如下图所示:


    image.png

重排序和happens-before规则

在执行程序时为了提高性能,编译器和处理器常常会对指令做重排序。重排序分三种类型:

  • 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
  • 指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism, ILP)来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
  • 内存系统的重排序。由于处理器使用缓存和读 / 写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。
    从 java 源代码到最终实际执行的指令序列,会分别经历下面三种重排序:
image.png

java 编译器禁止处理器重排序是通过在生成指令序列的适当位置会插入内存屏障(重排序时不能把后面的指令重排序到内存屏障之前的位置)指令来实现的。

happens-before

从 JDK5 开始,java 内存模型提出了 happens-before 的概念,通过这个概念来阐述操作之间的内存可见性。
如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须存在 happens-before 关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。

这里的“可见性”是指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。
如果 A happens-before B,那么 Java 内存模型将向程序员保证—— A 操作的结果将对 B 可见,且 A 的执行顺序排在 B 之前。

重要的 happens-before 规则如下:

  1. 程序顺序规则:一个线程中的每个操作,happens- before 于该线程中的任意后续操作。
  2. 监视器锁规则:对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。
  3. volatile 变量规则:对一个 volatile 域的写,happens- before 于任意后续对这个 volatile 域的读。
  4. 传递性:如果 A happens- before B,且 B happens- before C,那么 A happens- before C。

volatile关键字

volatile 可以说是 JVM 提供的最轻量级的同步机制,当一个变量定义为volatile之后,它将具备两种特性:

  1. 保证此变量对所有线程的可见性。而普通变量不能做到这一点,普通变量的值在线程间传递均需要通过主内存来完成。
    注意,volatile 虽然保证了可见性,但是 Java 里面的运算并非原子操作,导致 volatile 变量的运算在并发下一样是不安全的。而 synchronized 关键字则是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得线程安全的。
  2. 禁止指令重排序优化。普通的变量仅仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中的执行顺序一致。

引用计数算法(Reference Counting)

  • 给对象添加一个引用计数器,当有一个地方引用他,计数器加1,引用时效计数器减1,任何时刻计数器为0的对象都是不可再被使用的对象
  • 引用计数算法无法解决对象循环引用的问题

跟搜索算法(GC Roots Tracing)

  • 在实际的生产语言中,都是使用的根搜索算法判定对象是否存活
  • 算法的基本思路就是通过一系列的成为GC Roots的点作为起始进行向下搜索,当一个对象到GC Roots没有任何引用链(Reference Chain)相连,则证明此对象是不可用的
  • GC Roots
    -- 在jvm栈(帧中的本地变量)中的引用
  • 方法区的静态引用
  • JNI(native方法)中的引用

常见的GC算法

  • 标记-清理算法(Mark-sweep)
  • 标记-整理算法(Mark-compact)
  • 复制算法(copying)
  • 分代算法(generational)

标记-清理算法(Mark-sweep)

  • 算法分为“标记”和“清理”两个阶段,首先标记记出所有的需要回收的对象,然后回收所有需要回收的对象
  • 缺点
    -- 效率问题,标记、清理过程效率都不高
    -- 空间问题,清理后产生大量不连续的内存碎片,

标记整理(Mark-compact)

  • 标记过程一样,把所有存活对象一端移动,然后直接清理掉这一端边界外的内存

复制(copying)搜集算法

  • 将可用的内存的分为两块,每次只使用其中的一块,当半区内存用完了,仅将还存活的对象复制到另外一块上面,然后就把原来的整块内存空间一次性清理掉
  • 实现简单,运行高效,将内存缩小为原来的一半,代价高昂
  • 现在的商业虚拟机中都是使用了这一种算法来回收新生代

1、new关键字创建对象的3个步骤

  • 在堆内存中创建出对象的实例
  • 为对象实例的成员变量赋初值
  • 将对象的引用返回

指针碰撞

  • 前提是堆中的空间通过一个指针进行分割,一侧是已经被占用的空间,另一侧是未被占用的空间,

空闲列表

  • 前提是堆内存空间中已被使用的与未被使用的空间是交织在一起的,这时虚拟机需要通过一个列表来记录那些空间是可以使用的,那些空间是已被使用的,接下来找出可以容纳下新创建对象的且未被使用的空间,在此空间存储该对象,同时还要修改列表上的记录

对象在内存中的布局

  • 对象头
  • 实例数据(即我们再一个类中所声明的各项信息)
  • 对象填充(可选)

引用访问对象的方式

  • 使用句柄的方式
  • 使用直接指针的方式

-Xms5m -Xmx5m -Xss160k -XX:MaxMetaspaceSize=10M -XX:+HeapDumpOnOutOfMemoryError

jmap jstat jps jcmd
jcmd pid Thead.print:查看线程堆栈信息
jcmd pid GC.heap_dump filename:导出head_dump文件,可以使用jvisualum查看






引用类型

  • Strong默认通过object o=new object()这种方式赋值的引用
  • Soft、weak、phantom这三种都是继承reference
  • full gc
    -- soft:内存不够时一定会被gc,长期不用也会被gc
    -- weak:一定会被gc,当被Mark为dead,会在referenceQueue中通知
    -- phantom:本来就没引用,当从jvm heap中释放会通知

scavenge gc(minor gc)

  • 触发时机:新对象生成时,Eden空间满了

full gc

  • 对整个jvm进行整理,包括young,old和perm
  • 主要的触发时机:old满了,perm满了,system.gc()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335