第二章 java 内存区域 与内存溢出异常
2.2运行时数据区
方法区,堆==》所有线程共享的数据区
虚拟机栈、本地方法栈、程序计数器===》线程隔离使用的数据区
2.2.1:程序计数器
程序计数器是一块较小的内存,每条线程隔离使用,用于记录线程执行的行号。
多线程执行时轮流获取cpu时间片执行的,当线程恢复执行时,需要接着上一次执行的地址继续执行。
2.2.3 : 虚拟机栈
虚拟机栈是每条线程隔离使用,线程创建时会在栈中分配空间,线程销毁时该空间释放。
栈:先进会出的数据结构。
线程每执行进入一个方法,就会在线程栈中创建一个栈帧,成为当前帧,栈帧用于存储方法的局部变量表,局部变量表中存放着编译期可知的基本数据类型(byte,char,short,int ,long ,double,float,boolean) 和引用对象指针(对象存储在堆中)。为每一个方法创建的帧占用的空间在固定的,因为在编译即可知局部变量个数。
栈异常 StackOverFlowError :原因:线程请求的栈深度(方法调用深度)大于虚拟机允许的深度,与内存无关。
栈异常 OutOfMemoryError: 虚拟机栈空间可以动态扩展,扩展时无法申请到足够的内存
2.2.4 本地方法栈
与虚拟机栈,区别是用来执行native方法。
2.2.5 堆
堆 java 使用的最大的一块内存,用于存储对象实例和数组,所有线程共享。
推中的内存空间收GC回收,为了便于回收又划分为:新生代和老年代。
新生代包括:Eden空间、From Survivor 空间、To Survivor空间。
通过 -Xmx -Xms 指定推的大小。
异常:OutOfMemoryError 当堆中没有空间存储对象实例且堆无法扩展时触发。
2.2.6 方法区
用于存储 类的信息、常量、静态变量,是所有线程共享的一块内存区域。
在hotspot虚拟机中,方法区又叫永久代(持久代)
通过-XX:PermSize -XX:MaxPermSize 指定方法区大小
异常:OutOfMemoryError ,当方法区无法满足内存分配要求时触 发
2.2.6.1 运行时常量池
常量池是方法区的一部分。
Class文件除了有类的版本、字段、方法、接口等描述述信息外,还有常量池,用于存储编译时生
成的各种字面量和符号引用,这部分数据类加载后进入方法区的运行时常量池。
运行期也可以运态的在常量池中添加对象,如 String.intern()方法
String.intern()方法:
方法区内常量池中如果有同等对象直接返回,否则在方法区常量池中创建
String对象并返回。频繁在常量池中创建新字串,可能导至内存溢出。
2.2.7 直接内存
直接内存并不是由虚拟机管理的内存,如DirectByteBuffer ,在一些场景中可以提高性能。
2.3 hotspot虚拟机探秘
2.3.1对象的创建
a)分配空间
堆中为实例分配空间,存在线程安全问题:如A线程创建实例 ,B线程也创建实例,堆中空间有限,
为了避免相同空间分配给不同对象,可采用如下两种方式:
方式一: 分配空间时,使用CAS+重试的机制
方式二: 为每条线程在堆中分配一小块内存区域 ,称为本地线程分配缓冲(TLAB),线程创建的实例,优先在此区域分配空间,避免冲突。 只有TLAB空间被占满时,才使同步锁,顺序的分配堆空间。
是否启用TLAB,使用JVM参数:-XX+/-UseTLAB参数来指定
b) 实例空间分配完成后,将该空间初始化为0.保证了实例在未初始化时就可以使用,值都为0.
(不包括对象头)
c) 初始化对象头,如这个实例是哪个类的,如何找到类的元数据信息,实例的哈希码,GC的分代年龄等信息。
d) 初始化方法被调用(构建器及初始化代码块)
2.3.2 对象实例的内存布局
在堆中,一个实例的内存布局分成三部分:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。
对象头数据分为两部分:
a) 对象自身的运行时数据:哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID,偏向时间戳。
这部分数据占用空间:32位虚拟机占用32bit。64位虚拟机占用64bit 。
这部分空间,官方称为:Mark Word
b) 元数据类型指针,即指向这个对象实例的类的信息
实例数据:存储程序代码中定义的各种属性的值。包含从父类中继承下来的。
对齐填充:不是必须有的,启占位符的作用,原因:HotSpot虚拟机要求实例的起始地址是8的整数倍。
2.3.3 对象的访问定位
在HotSpot虚拟机中: 栈中 对象引用变量直接指向堆中实例的地址。
第三章 垃圾收集器与内存分配策略
3.2. 对象实例已死的判定算法
堆中对象实例不再使用了,需要释放该实例占用的空间,那么如何判定实例不在使用?
3.2.1 引用计数法
给对象添加一个引用计数器,每当有一个地方引用它:计数器+1,当引用失败时:计数器-1,
任何时刻计数器为0的就是不可再引用对象。
java虚拟机未采用 引用计数法,原因如下:如两个实例相互引用对方,除此之外没有被任何地方引用,实现上这两个对象该被释放空间了,但按引用计数法,它们还是有用的。
3.2.2 可达性分析算法
通过一系列称为“GC roots" 的对象为起始点,从这些节点开始按引用关系搜索,搜索所走过的路径称为引用链,当一个对象到GC roots 没有任何引用链相连,则认为该对象是不可用的。
java 中可以做为GC roots 的对象为:
a):虚拟机栈中本地变量表引用的对象
b):方法区中静态变量引用的对象
c):方法区中常量引用的对象
d):本地方法栈(java 中native方法)中引用的对象
3.2.3 引用类型分类
1)强引用
指程序中普遍存在的类似于 "Object obj = new Object()" 这类引用,只要强引用还存在
垃圾回收器永远不会回收。
2)软引用
描述有用但不是必须的对象。在发生内存溢出之前进行垃圾回收。
3)弱引用
描述非必须的对象,垃圾回收器执行时,不管内存是否足够都 进行回收。
4)虚引用
最弱的一种引用关系,为虚引用关联一个对象实例的唯一目的,是产生垃圾回收时得到一
个系统通知。
3.3 垃圾回收算法
3.3.1 标记-清除算法
1:首先找到GC roots不可达对象,对其标记
2: 对其清理
缺点: 1.标记、清理 :这两个过程的效率不高
2. 产生大量不连续的内存碎片,不利于之后为对象分配空间。
3.3.2 复制算法
为了解决标记清除的效率问题,产生了复制算法。
它将可用内存划分为大小相同的两块,每次只使用其中一块,当这一块用完了就将还存活的
复制到另一块内存上,使用的内存块一次清理掉。这样每次只对一半内存区回收,内存分配也不
考虑内存碎片问题,只是移动堆顶指针,按顺序分配空间。
缺点:内存使用原来的一半。
进一步优化后:
新生代+老年代。
新生代:大块eden空间 + 2个小块相等的Survivor空间。
每次只使用eden 和其中一个Survivor空间,回收时把eden和Survivor上的存活对象复制到
另一个Survior上,最后清理掉Eden 和使用的Survivor.
HotSpot默认 Eden与 一个Survivor 比例为 8:1,也就是新生代中90%的空间可用,只浪费 10%的空间。这些都是基于IBM的研究:98%的对象都是朝生夕死的。
如果另一个Survior上,没有空间存放所有的存活对象,会通过分配担保机制进入老年代。
3.3.3 标记-整理算法
复制算法不适用于老年代,因为该空间内的对象存活比例较大,需要额外的另一块内存空间来复制。
标记-整理算法: 先标记不可达对象(找到存活对象),从老年代的某一端开始,复制所有存活对象,依次分配内存。
3.3.4 分代收集算法
新生代中每次垃圾收集都会有大量对象死去,只有少量存活,那就选择复制算法
老年代中存活率高,没有额外的空间复制,所有使用 标记-清理 或标记-整理算法。
3.4 HotSpot算法
3.5 HotSpot 垃圾回收算法
3.5.1 Serial 收集器
Serial 收集器 是单线程垃圾回收器采用单线程方式,停止所有用户线程进行垃圾回收。
Serial 收集器: 回收新生代采用 复制算法。
Serial old收集器: 回收老年代采用 复制整理算法。
3.5.2 ParNew 收集器
ParNew收集器其实是Serial收集器的多线程版本,采用多线程方式回收垃圾,暂集所有用户线程。
ParNew是新生代的垃圾回收器,默认线程数==CPU数量。 (在cpu<=2的情况下,并不能比Serial收集器快),可以使用能数-XX:ParallelGCThreads指定线程数。
ParNew 收集器采用复制算法。
3.5.3 Parallel Scavenge 收集器
Parallel Scavenge 收集器:回收新生代,采用复制算法,多线程回收,暂停工作线程。
Parallel Scavenge收集器与其它收集器 的不同点:
其它收集器是:尽可能缩短垃圾回收时工作线程的暂停时间。
Parallel Scavenge:提高吞吐量。
吞吐量: cpu运行用户代码的时间/cpu总的消耗时间,即
cpu运行用户代码的时间/cpu运行用户代码时间+垃圾回收时间。如虚拟机总运行时间100分钟,垃圾回收占用1分钟,吞吐量为 99%。
高吞吐量:可以高效率的利用CPU,尽可能的完成程序运算的任务,适合后台计算,不需要太多交互的任务。
其它收集器:适合与用户交互的程,停顿时间越短,良好的响应速度提升用体验。
3.5.4 Serail old收集器
Serail 收集器:回收老年代,采用复制整理算法,单线程回收,暂停工作线程。
3.5.5 Parallel old 收集器
Parallel old 收集器:回收老年代,采用复制整理算法,多线程回收,注重吞吐量,暂停工作线程。
注重吞吐量应用:新生代使用 Parallel Scavenge ,老年代使用Parallel old 收集器。
3.5.6 CMS收集器
CMS收集器:回收老年代,采用标记-清理算法,多线程回收,注重最短停顿时间,与工作线程并发执行。
第十二章 java 内存模型与线程
12.3 java内存模型
java 所有数据存储在主内存中,线程要对某个变量操作时,从主内存中加载数据在工作内存,再由工作内存中读取,再操作,操作完成(赋值)写回工作内存,再从工作内存写回主内存。
工作内存的每条线程私有的,因此多线程同时对于一个变量的操作,可能产生线程安全问题。
多线程间数据交互通过主内存实现的。
工作内存与主内存之间的交互协议:
1.lock (锁定):作用于主内存的变量,它把变量标识为一条线程独占状态
2.unlock(解锁):作用于主内存变量,它把处于锁定状态的变量释放出来,释放后变量才可以被其它线程锁定。 (同一线程程可以对一个变量lock N次,也必须unlock N次)
3.read (读取):作用于主内存变量,它把一个变量的值从主内存传给工作内存,以便之后的load操作使用。
4.load(加载):作用于工作内存变量,它把read操作得到的变量放到工作内存的变量副本中。
5.use(使用):作用于工作内存变量,它把工作内存中的变量传给执行引擎。每当jvm遇到一个需要使用变量值的指令时执行此操作。
6.assign(赋值):作用于工作内存变量,它把从执行引擎接收到的值赋值给工作内存变量。当jvm遇到一个赋值指令时执行此操作。
7.store(存储):作用于工作内存变量,它把工作内存中的变量传给主内存,以便之后的write 操作使用。
8.write(写入):作用于主内存变量,它把store操作的值写入主内存。
锁相关规定:
1.一个变量,同一时刻只允许一线程对其进行lock操作,lock操作可以被同一线程执行多次,多次执行lock后,只有执行相同次数所unlock,变量才会被解锁。
2.对一个变量执行lock操作,会清空工作内存中的值,在执行引擎使用这个变量时,需要重新执行load<---read 操作 加载值。
3.一个变量unlock之前必须,对其执行store-->write写回主内存。
volatile型变量:可见性
1.每次使用volatile变量时,都必须从主内存刷新最新值到工作内存,再操作,用于保证能够看见其它线程对volatile变量的修改值。
2.每次对volatile 变量值修改时,都 必须立刻同步到主内存中,用于保证其它线程能够看到自己对volatile变量的修改值。
3.volatile变量不会被指令重排序优化:即代码执行顺序与程序中的顺序机同。
普通型变量:
1.每次使用变量时,不一定会从主内存刷新最新值,比如当前工作内存中就该变量。
2.每次对变量值修改时,可能不会立刻同步到主内存中。
可见性 :当一个线程修改了一个共享变量值,其它线程能够及时得知这个修改。
1.volatile 保证变量读取时从主内存取值-》工作内存-》执行引擎
赋值时执行引擎--》工作内存-》立即写入主内存。
2.synchronized : 锁定时,清空工作内存此变量的值,从主内存读取,unlock之前,把值写回主内存。
3.不可变对象及final: 变量不可变,对可见性无影响。
禁止指令重排优化:
1.volatail
2.synchronized
synchronized具有: 可见性,原子性(线程串行执行),禁止指令重排优化,
volatail具有:可见性, 可见性,非原子性,禁止指令重排优化
12.4 线程
1.线程调度
分配CPU时间片执行
2.线程状态
a)新建状态 ,Thread被创建,但未start
b)可运行状态: 线程启动进入可执行队列,但未分配cpu时间片执行
c) 运行状态:线程在运行
d) Waiting :这种状态下线程不会被分配cpu时间片,在等待其它线程唤醒,以下方法会进入waiting状态:
1.没有timeout 参数的Object.wait
2.没有timeout参数的Thread.jon
3.:LockSupport.park
e)TimedWaiting :这种状态下线程不会被分配cpu时间片,在等待其它线程唤醒或超时,以下方法会进入TimedWaiting状态:
1.Thread.sleep()
2.设置了Timeout参数的Object.wait(TimeOut)
3.设置了Timeout参数的Thread.join(TimeOut)
4.LockSupport.parkNanos(long nanos)
5.LockSupport.parkUntil(long deadline)
f)Blocked阻塞状态:
线程在等待一个排他锁(synchronized 锁)
g)结束状态
线程执行完成
第十三章 线程安全与锁优化
13.1 线程安全的实现方法
1.互斥同步
synchronized 和ReentrantLock,都是可重入锁具有机同的内存语义,synchronized 语法层面,ReentrantLock API层面上的
两者区别:
1.等待可中断
synchronized 会一直等待持有锁的线程释放锁
ReentrantLock.lockInterruptibly()
会一直等待持有锁的线程释放锁,或其它线程中断此线程。
2.公平锁
synchronized 是非公平锁
ReentrantLock默认也是非公平锁,可以设置为公平锁
3.锁绑定多个条件
synchronized 只允锁定对象为一个隐含的条件,调用其.wait .notify.notifyAll
ReentrantLock 可以通过newCondition 创建多个条件
4. ReentrantLock 可以使用tryLock方法判定是否其它线程持有锁。
适用于多个线程有一条线程处理就可以的场景。
5.同步代码中异常时,synchronized会自动释放锁,ReentrantLock需要在代码finally里释放锁。
2.使用CAS (compare and swap)
此方式不会阻塞线程,性能比互斥锁好,适用于 极短时间内简单操作。
CAS 需要3个参数:变量在内存中的地址,原值,新值。
如果原值与内存地址中的值相同,则用新值替换,否则失败。
CAS存在"ABA"问题。
3.不使用同步机制
3.1 不使用共享数据
方法涉及的数据都是私有或不可变的。
3.2.不可变对象
多线程访问同一个不可变对象是安全的。
3.3. 线程内对象
ThreaLocal
相关推荐
这个压缩包“JAVA-JVM-全面/发展史/GC.zip”显然包含了关于Java、JVM、垃圾回收(GC)及其历史的深入学习资料,特别是通过“JAVA-笔记.pdf”这样的文档形式。 Java的发展史可以追溯到1991年,由Sun Microsystems的...
"jvm视频及笔记"这个资源显然是一份全面学习JVM的材料,结合了视频教程和书面笔记,帮助学习者深入理解JVM的工作原理及其在实际开发中的应用。 JVM的学习可以从以下几个重要的知识点开始: 1. **JVM架构**:JVM...
### 深入解析 JVM 内存区域 #### 一、Java内存区域概述 Java虚拟机(JVM)作为Java程序的运行环境,负责管理和分配内存...理解这些区域对于深入学习JVM原理至关重要,也有助于开发者编写更加高效和可靠的Java应用程序。
《JVM:深入理解Java虚拟机》是一本深入解析Java虚拟机工作原理和技术细节的经典书籍。这份学习笔记将涵盖JVM的关键概念、架构以及它如何影响Java程序的性能。我们将探讨以下几个方面: 1. **JVM概述** Java虚拟机...
《深入理解JVM内存区域》 Java虚拟机(JVM)是Java语言的运行环境,支持多种语言,包括Scala、Kotlin、Groovy等。虚拟机历史了解即可,无需关注Hotspot。 JVM内存区域主要分为五部分:程序计数器、虚拟机栈、本地...
《JVM笔记(阳哥)》是一份深入探讨Java虚拟机(JVM)的资料,由阳哥精心整理。这份笔记涵盖了JVM的基础概念、内存管理、类加载机制、性能优化等多个方面,对于理解Java程序的运行机制以及提升开发效率具有重要的...
《深入理解JVM:从实践到精通》 Java虚拟机(JVM)是Java程序运行的基础,它将编译后的字节码转换为机器码,实现了跨平台的运行能力。本学习笔记旨在全面解析JVM的工作原理,涵盖内存管理、类加载机制、垃圾收集、...
此外,通过深入理解字节码指令,开发者可以更深刻地了解 Java 语言的行为,并利用这些知识来优化代码性能。 最后,理解 Java 虚拟机是如何处理 Class 文件和字节码对于提高程序性能和调试效率至关重要。这不仅仅是...
除了上述提到的基础知识点外,《深入理解Java虚拟机——JVM高级特性与最佳实践(第2版)》这本书籍还深入探讨了JVM的性能调优、并发编程、以及各种高级特性的具体应用。比如,对于性能调优,书中讲解了如何根据不同的...
读书笔记:深入理解Java虚拟机JVM高级特性与最佳实践第3版学习笔记
读书笔记:深入理解Java虚拟机 JVM高级特性与最佳实践第二章笔记
读书笔记:《深入理解Java虚拟机JVM高级特性与最佳实践》笔记和理解。本笔记主要以图的形式记录。
【狂神说JVM探究】是一份集合了...这些知识点覆盖了JVM的基础到进阶内容,适合初学者和有一定经验的开发者深入理解Java运行机制。通过学习这份资料,读者可以系统地掌握JVM的工作原理,并提升Java应用的性能优化能力。
读书笔记:深入理解Java虚拟机JVM高级特性与最佳实践第3版 周志明 学习笔记
读书笔记:深入理解Java虚拟机JVM高级特性与最佳实践
读书笔记:深入理解Java虚拟机 JVM高级特性与最佳实践
【Java分布式应用学习笔记-谈JVM】 在Java分布式应用中,JVM(Java虚拟机)扮演着至关重要的角色。虽然有些人可能认为分布式系统与JVM的关系并不密切,但事实上,尤其是在大型分布式环境,如云计算服务平台,对Java...
《Eclipse性能优化——<深度理解JVM>读书笔记》主要涵盖了如何利用Eclipse IDE进行Java应用程序的性能优化,以及深入理解JVM的工作原理。在本文中,我们将探讨Eclipse的性能调优策略,以及JVM内存管理和垃圾回收机制...
读书笔记:深入理解Java虚拟机 JVM高级特性与最佳实践第3版
读书笔记:深入理解java虚拟机JVM高级特性与最佳实践中代码