深入理解Java虚拟机
结合书籍《深入理解Java虚拟机》
阐述JVM工作原理、JVM高级特性
并结合简单的例子演示
一只老风铃
欣于所遇,暂得于己,快然自足。
展开
-
深入JVM 高并发-锁
——自旋锁与自适应自旋互斥同步对性能最大的影响是阻塞的实现,挂起线程和恢复线程需要切换到内核态来完成,这些操作给系统的并发性能带来很大的压力。但是在许多应用上,共享数据的锁定状态只会持续一小段时间,为了这段时间去挂起和恢复线程不值得,如果物理机器有一个以上的处理器,能让2个或以上线程同时并行执行,就可以让后面请求锁的线程稍等一下,但不放弃处理器的执行时间,看持有锁的线程是否很快释放锁,为了让...原创 2019-03-18 15:22:11 · 405 阅读 · 0 评论 -
深入JVM 线程安全的实现
——互斥同步互斥同步(Mutual Synchronization)是常见的一种并发正确性保障手段,同步是指多线程并发访问共享数据时,保证共享数据同一时刻只能被一个(或一些 信号量)线程使用。而互斥是同步的一种手段,临界区、互斥量、信号量都是主要的互斥实现手段,互斥是手段,同步是结果,互斥是方法,同步是目的在Java语言层面,最基本的互斥同步手段就是synchronized关键字,sync...原创 2019-03-17 14:18:48 · 215 阅读 · 0 评论 -
深入JVM 线程安全分级
线程安全:当多个线程访问一个对象,如果不需要考虑这些线程在运行时环境的调度和执行,也不需要额外的同步,或者调用方进行协调,调用执行的结果总是正确的,那么这个对象是线程安全的在Java中按照安全等级可以将共享数据划分成5类——不可变不可变的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要采用任何线程安全保障,final关键字:只要一个不可变对象被创建出来,那其外部的可...原创 2019-03-17 13:29:29 · 171 阅读 · 0 评论 -
深入JVM 先行发生原则
Java内存模型有序性可以依靠volatile和synchronized关键字来完成,但实际进行Java程序设计时却并没有过多注意这一点,因为Java语言存在一个先行发生原则:happens-before原则。这个原则用于判断数据是否存在竞争,线程是否安全的判断依据。所谓先行原则,指的是两项操作之间的偏序关系,如果说操作A先行发生于操作B ,即操作B发生之前,操作A产生的影响能被操作B观察到,...原创 2019-03-17 12:48:47 · 402 阅读 · 0 评论 -
深入JVM 原子性、可见性、有序性
Java内存模型围绕着并发过程中如何处理原子性、可见性和有序性三个特征来建立的——原子性由Java内存模型来直接保证原子性操作包括Read Load Assign Use Store Write可以认为基本数据类型的读写访问具备原子性,如果应用场景需要一个更大范围的原子性保证,Java内存模型提供Lock Unlock操作来提供支持,尽管虚拟机未把Lock Unlock操作直接开放接口使用...原创 2019-03-16 13:52:27 · 587 阅读 · 0 评论 -
深入JVM 内存间交互操作
Javan内存模型定义了8种操作来完成主内存与工作内存的读写交互,虚拟机实现保证每一种操作都是原子的,不可再分的Lock锁定 作用于主内存变量,将变量标志为一条线程所独占 Unlock解锁 作用于主内存变量,将处于锁定的变量释放出来 Read读取 作用于主内存变量,它将一个变量的值从主内存传输到线程的工作内存中 Load载入 作用于工作内存变量,它把从主内存...原创 2019-03-14 10:33:13 · 785 阅读 · 0 评论 -
深入JVM Java内存模型
Java虚拟机规范试图定义一种Java内存模型来屏蔽掉各种硬件操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致性的内存访问效果。而相对而言,C C++是直接使用物理硬件和操作系统的内存模型,因此在不同的平台上存在差异,可能导致程序在不同平台执行效果不同,存在耦合的弊端定义Java内存模型需要考虑得当,必须足够严谨,才能让Java的并发内存访问不出错,但是,也必须足够宽松,使...原创 2019-03-13 17:47:19 · 289 阅读 · 0 评论 -
深入JVM 硬件的效率与一致性
让计算机并发执行若干个运算任务与更充分利用计算机的效能之间的因果关系,看似顺理成章,实际它们之间关系远远没有想象的简单,其中一个重要的复杂性来源于绝大多数的运算任务不可能单靠 处理器计算完成,那么处理器与内存的交互,如读取运算数据,存储运算结果,类似的IO操作是很难消除的(无法仅仅依靠寄存器完成所有计算任务)由于计算机的存储设备与处理器之间的速度代沟,所以现代计算机系统不得不加入一层读写速度尽...原创 2019-03-13 17:29:21 · 286 阅读 · 0 评论 -
深入JVM invokedynamic指令
某种程度上讲 invokedynamic指令和MethodHandle机制的作用是一样的,都是为了解决分派机制固化在虚拟机的特点,让用户有更高的自由度是其设计的目的。MethodHandle类似,只不过methodHandle采用Java语言和API进行实现,invokedynamic则是字节码来完成。每一个含有invokedynamic指令的位置称为动态调用点dynamic Call sit...原创 2019-03-02 13:24:50 · 523 阅读 · 0 评论 -
深入JVM MethodHandle
——动态语言类型动态类型语言的关键特征是它的类型检查主体过程发生在运行期而不是编译期,如: python javascript PHP等,相对的,编译期需要进行类型检查的语言有C++ Java——obj.println("test");对于这段代码,静态类型语言如Java编译期就会检查obj是否是printStream类型范围,而对于javascript语言则不会检查,它直到运行期间才...原创 2019-03-01 20:49:09 · 472 阅读 · 0 评论 -
深入JVM 动态分派
动态分派与多态性的另一个重要体现: 重写(Override) 有着十分密切的关系。一个动态分派的例子:public class DynamicDispatch { static abstract class Human { protected abstract void say(); } static class Man extends Human { @Overr...原创 2019-03-01 20:10:54 · 247 阅读 · 0 评论 -
深入JVM 静态分派
分派调用的过程揭示了面向对象语言的多态性特征,其中重载和重写是其重要组成部分一个静态分派例子:public class StaticDispatch { static abstract class Human { } static class Man extends Human { } static class Woman extends Human {...原创 2019-03-01 19:51:58 · 196 阅读 · 0 评论 -
深入JVM 方法调用-解析
方法调用不等同于方法执行,方法调用阶段唯一的任务是确定被调用方法的版本,暂时不涉及内部的具体执行过程。在程序运行过程中,方法调用是最频繁的操作。但Class文件的编译过程并不包括传统编译的连接步骤,一切方法调用在Class文件里存储的都是符号引用,而不是方法在实际运行的内存布局的入口地址(相当于直接引用)。这个特性使得Java带来了强大的拓展能力,但也使得Java方法调用过程变得复杂起...原创 2019-02-27 16:43:34 · 215 阅读 · 0 评论 -
深入JVM 栈帧数据结构
——局部变量表局部变量表(Local Variadble Table)是一组变量值存储空间,用于存放方法参数和方法内定义的局部变量。在Java程序编译为Class文件时,在方法的Code属性的max_locals数据项中确定该方法所需要分配的局部变量表的最大容量局部变量表的容量以变量槽(slot)为最小单位,虚拟机规范中没有规定槽的大小,它允许具体数据类型随着处理器、操作系统而大小发生改变...原创 2019-02-26 16:26:29 · 720 阅读 · 0 评论 -
深入JVM 运行时栈帧结构
栈帧(stack Frame)是用于支持虚拟机进行方法调用和执行的数据结构,它是虚拟机运行时数据区中虚拟机栈的栈元素。栈帧存储方法的局部变量表,操作数栈,动态连接,方法返回地址等信息。每一个方法的执行过程,都对应着一个栈帧入栈和出栈的过程每一个栈帧需要的各部分数据结构,在编译程序代码时,都已确定并写入方法表的Code属性中,因此一个栈帧需要分配多大的内存,不会受到程序运行期间变量数据的影响,而...原创 2019-02-26 15:44:12 · 269 阅读 · 0 评论 -
深入JVM 双亲委派模型
从虚拟机角度看,只存在2种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身一部分; 一种是所有其他的类加载器,使用Java语言实现,独立于虚拟机,继承于java.lang.ClassLoader从Java开发人员的角度来看,类加载器可进一步划分,一般情况下提供3种系统的类加载器——启动类加载器(Bootstr...原创 2019-02-26 15:24:24 · 3639 阅读 · 0 评论 -
深入JVM 类加载器
类加载阶段的通过一个类的全限定名获取该类的二进制字节流这个动作可以人为的放到虚拟机外部实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为 类加载器类加载器是Java语言的创新特性,也是其流行的原因,类加载器在类层次划分,热部署,代码加密等领域大放异彩,成为Java技术体系的一个重要组成部分类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用远不止类加...原创 2019-02-26 14:23:12 · 191 阅读 · 0 评论 -
深入JVM 类加载-初始化
类的初始化阶段是类加载的最后一步,前面的类加载过程中,除了在加载阶段用户可以自定义类加载器参与外,其余动作由虚拟机完全控制和主导。到了初始化阶段,才真正开始执行类中定义的Java程序逻辑在准备阶段,部分变量已经赋予了一次系统要求的初始值,而在初始化阶段,根据程序设计者的主管计划去初始化类变量和其它资源,或者可以从另外一个角度表述:初始化阶段是执行类构造器<clinit()>方法的过...原创 2019-02-25 13:44:12 · 176 阅读 · 0 评论 -
深入JVM 解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程——符号引用:符号引用以一组符号来描述所引用的目标。符号可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可。符号引用与虚拟机1的内存布局无关,引用的目标不一定加载到内存中。各种虚拟机实现的内存布局不相同,符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中——直接引用:直接引用可以是直接指向目标的指针,相对偏...原创 2019-02-25 13:26:27 · 196 阅读 · 0 评论 -
深入JVM 类加载-验证
验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合虚拟机的规范要求。Java语言本身是相对安全的语言,使用纯粹的Java代码无法做到访问数组边界以外的数据,将一个对象类型转化为未实现的类型,跳转到不存在的代码处,如果这样做,javac编译期间将拒绝编译。但是Class文件并不一定由Java语言编译而来,可以通过任何途径获取。在字节码语言层面,上述Java代码...原创 2019-02-25 13:06:58 · 827 阅读 · 0 评论 -
深入JVM 类加载-加载
加载是类加载过程的第一个阶段(需要区分类加载与加载).在加载阶段,虚拟机主要完成3件事——通过一个类的全限定名获取此类的二进制字节流 ——将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构 ——在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类各种数据的访问入口虚拟机具体实现这些过程都是灵活可变的。如通过类的全限定名获取二进制字节流,并没有规定一定...原创 2019-02-25 12:46:16 · 151 阅读 · 0 评论 -
深入JVM 对类的被动引用
Java虚拟机规范规定了5种必须对类进行初始化(加载)的场景,除此之外引用类的方式称为被动引用,将不会触发类的初始化——通过子类引用父类的静态字段,不会触发子类的初始化class SuperClass{ static { System.out.println("SupreClass init"); } public static int value=123; }c...原创 2019-02-24 16:20:48 · 189 阅读 · 0 评论 -
深入JVM 类加载的时机
类从被加载到虚拟机内存开始,直到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用、卸载等7个过程,其中验证、准备、解析部分称为连接其中 加载、验证、准备、初始化、卸载相对顺序是确定的,而解析阶段不一定,它可以在初始化阶段之后进行,这些阶段是交互进行的,通常会在一个阶段执行过程中调用激活下一阶段类加载的时机并没有具体的确定规则,而虚拟机规定了有且只有5种情况...原创 2019-02-24 16:02:12 · 199 阅读 · 0 评论 -
深入JVM 平台无关性与语言无关性
——通常所指的代码编译是一个从程序翻译成机器所能识别的二进制机器码的过程。然而越来越多的程序语言选择了与具体操作系统和机器指令集无关,平台中立的格式作为程序编译的存储格式——与平台无关的构想最终实现在操作系统的应用层上,所谓不同平台的虚拟机都可以载入和执行同一种平台无关的字节码,从而实现一次编写,到处运行的目的字节码(bytecode)是构成平台无关性的基石,Java虚拟机载入并执行统一的...原创 2019-02-24 15:18:23 · 1302 阅读 · 0 评论 -
深入JVM JConsole监视工具
Jconsole(Java Monitoring and Management Console)是一种基于JMX的可视化监视,管理工具。位于JDK/bin目录下,通过 “jconsole.exe”启动JConsole后,可自动检索处本机的虚拟机进程连接后,主要包括内存,线程,类,等子面板——内存监视这段程序以64KB/50毫秒的速度向Java堆中填充数据,一共执行1000次...原创 2019-02-24 13:49:18 · 256 阅读 · 0 评论 -
深入JVM 长期存活对象将进入老年代
——长期存活对象进入老年代虚拟机采用分代收集的思想来管理内存,内存回收时必须识别哪些对象放入新生代,哪些对象放入老年代。为了做到这点,虚拟机为每个对象定义了一个对象年龄计数器。如果对象在Eden出生并经过一次Minor GC仍然存活,并且能被Survivor容纳,将被移动到Survivor区,并且对象年龄设置为1.对象每经过一次Minor GC后仍保持存活,年龄+1当对象年龄到达一定程...原创 2019-02-23 16:32:58 · 1718 阅读 · 0 评论 -
深入JVM 对象优先在Eden分配
前言:对象的内存分配,大方向讲,指的是对象在堆上分配,对象的主要分配发生在新生代的Eden区,当然少数分配在老年代,分配的规则并不是固定不变的,细节取决于具体的虚拟机实现。——对象优先在Eden区分配大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发生一次Minor GC虚拟机提供 -XX:PrintGCDetails这个收集器日志参数,告诉虚拟...原创 2019-02-23 15:46:47 · 2262 阅读 · 1 评论 -
深入JVM GC收集算法概述
——标记-清除算法最基础的收集算法时“标记-清除”算法,算法分为标记和清除2个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象,标记过程也就是之前的2次标记过程,该算法是最基础的算法,因为其它算法都是在其基础上进行改进算法主要不足:算法效率低下,标记和清除2个过程效率性能低下,另外空间也存在问题,标记清除后将产生大量的不连续的内存碎片,空间内存碎片太多导致之后需要分...原创 2019-02-22 21:53:45 · 163 阅读 · 0 评论 -
深入JVM GC对象的自救
Java虚拟机通过可达性分析等算法探测堆中不可达的对象,当一个对象通过可达性算法不可达时,并非立即被回收,而是暂时处于在“缓刑”状态,而真正要宣告一个对象死亡,必须经历至少2次标记过程——如果对象在进行可达性分析后发现与GC Roots引用链不相连时,那么它会被第一次标记并且进行一次筛选,筛选的条件是此对象是否需要执行finalize()方法。当对象没有覆盖finalize()方法,或者fin...原创 2019-02-22 21:05:12 · 410 阅读 · 0 评论 -
深入JVM 可达性分析与引用分类
主流的程序设计语言(Java C#等)在确定回收对象的主流实现中,是通过 可达性分析(Reachability Analysis) 来判断对象是否存活。该算法的基本思路是通过一系列 GC Roots 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Referance chain),当一个对象到GC Roots没有任何引用链(即GC Roots 到这些对象不可...原创 2019-02-21 15:07:13 · 502 阅读 · 0 评论 -
深入JVM 引用计数法
在堆中存放着对象实例,GC回收器在对堆进行回收前,需要确定哪些对象需要被回收,即确定哪些对象还存活,哪些对象已经死去(即不可能在被任何途径使用的对象)——引用计数法 前言:给对象添加一个引用计数器,每当一个地方引用它时,计数器加1,每当引用失效时,计数器减少1.当计数器的数值为0时,也就是对象无法被引用时,表明对象不可在使用,这种方法实现简单,效率较高,大部分情况下不失为一个有效的方法...原创 2019-02-21 14:42:50 · 1886 阅读 · 1 评论 -
深入JVM GC概述
GC(Garbage Collection) 是Java语言的伴生产物,而内存回收也是Java区分C C++的一大特征,使得程序设计过程无需过多考虑内存回收 泄漏等问题,使得内存分配 自动化成为可能GC主要解决3个问题:哪些内存需要回收? 什么时候回收? 怎么回收?Java运行时数据区域其中的几大部分:包括 程序计数器、虚拟机栈、本地方法栈 这3个区域是线程私有的,也就是生命周...原创 2019-02-21 14:14:43 · 302 阅读 · 0 评论 -
深入JVM 方法区和常量池溢出
Java虚拟机运行数据区中,常量池属于方法区内部,主要存放类加载的元信息、常量、静态变量、即时编译的代码,还有编译后生成的字面值 符号引用等而最经典的String.intern()方法是一个native本地方法,它的作用:如果字符串常量池中已经等于此String的对象字符串,则返回常量池中的这个String对象;否则,将此String对象放入常量池,并返回String对象的引用案例演示: ...原创 2019-02-20 23:27:42 · 1343 阅读 · 0 评论 -
深入JVM 虚拟机栈和本地方法栈溢出
由于在HotSpot虚拟机中不区分虚拟机栈和本地方法栈,因此,对于HotSpot而言,-Xoss设置本地方法栈的参数失效,栈的容量大小只有Xss参数设置。虚拟机栈主要出现2种错误异常如果线程请求的栈深度大于虚拟机栈所允许的最大深度,抛出StackOverFlowError错误异常 如果虚拟机栈在拓展栈时无法申请到足够的内存空间,抛出OutOfMemoryError错误异常实验限制于单线...原创 2019-02-20 22:54:33 · 1067 阅读 · 0 评论 -
深入JVM 实战堆内存溢出
在Java运行时数据区域中,除程序计数器外,其它几个运行时区域都有可能发生OutOfMemoryError错误异常 ——虚拟机启动可以设定虚拟机启动参数,以Eclipse IDE为例,虚拟机启动参数 VM Args 将影响虚拟机内存空间 1 Java堆溢出 在主线程中不断创建对象,设置虚拟机启动参数VM Args: -Xms20m -Xmx 20m -XX:+HeadDu...原创 2019-02-20 22:17:50 · 222 阅读 · 0 评论 -
深入JVM 对象的内存布局与访问定位
在经典Java 虚拟机HotSpot中,对象在堆中的布局分为3块:对象头(object header)、 实例数据(Instance Data)、 对齐填充 HotSpot虚拟机的对象头包括两部分第一部分是用于存储对象自身的运行时数据,如哈希码(hashcode)、GC分代年龄、锁相关信息,这部分数据长度在32位和64位中分别是32bit 64bit 称为 Mark Word 。...原创 2019-02-20 21:44:14 · 258 阅读 · 0 评论 -
深入JVM 对象的创建
——Java作为纯面向对象的语言,运行过程无时无刻都有对象被创建。在语言层面上,创建对象(克隆、反序列化)通常仅仅体现为new关键字,而虚拟机中实际创建过程则更为复杂。 虚拟机遇到一条new指令后,首先检查这个指令的参数是否能在常量池种定位到这个类的符号引用,并且检查这个符号引用代表的类是否被类加载、解析、和初始化,否则,必须先进行相应的类加载过程。 类加载检查完后,紧接...原创 2019-02-20 20:49:53 · 117 阅读 · 0 评论 -
深入JVM 运行时数据区域
——Java虚拟机在执行Java程序时将其所管理的内存划分为若干区域,各个区域都有各自的用途,以及创建和销毁的时间,有的区域随虚拟机进程的启动存在,有的依赖于用户线程的启动和结束而建立和销毁——Java虚拟机所管理的内存包含以下几个运行时数据区域——程序计数器pc程序计数器是一块占用空间较小的区域,是当前线程执行的字节码的指示器。在虚拟机的概念模型中,字节码解释器工...原创 2019-02-20 20:13:52 · 143 阅读 · 0 评论