本文转载自:JVM 看这一篇就够了

javap工具生成非正式的 ”虚拟机汇编语言“,格式如下:
[[]…]] [comment]
是指令操作码在数组中的下标,该数组以字节形式来存储当前方法的Java虚拟机代码;也可以是相当于方法起始处的字节偏移量
是指令的助记码、是操作数、是行尾的注释
constant_pool_count:是从1开始的
不同的常量类型,用tag来区分,它后面对应的 info 结构是不一样的
L表示对象,[ 表示数组、V表示void
stack:方法执行时,操作栈的深度
Locals:局部变量所需的储存空间,单位是slot
slot是虚拟机为局部变量分配内存所使用的最小单位
args_size:参数个数,为1的话,因实例方法默认会传入this,locals也会预留一个slot来存放
ASM Core ApI 中操纵字节码的功能基于 ClassVisitor 接口。这个接口中的每个方法对应了 class 文件中的每一项
ASM 提供了三个基于 ClassVisitor 接口的类来实现 class 文件的生成和转换
ClassReader:ClassReader 解析一个类的 class 字节码
ClassAdapter:ClassAdapter 是 ClassVisitor 的实现类,实现要变化的功能
ClassWriter:ClassWriter 也是 ClassVisitro 的实现类,可以用来输出变化后的字节码
ASM 给我们提供了 ASMifier 工具来帮助开发,可使用ASMifier 工具生成 ASM 结构来对比

加载:查找并加载类文件的二进制数据
连接:就是将已经读入内存的类的二进制数据合并到 JVM 运行时环境中去,包含以下步骤:
验证:确保被加载类的正确性
准备:为类的 静态变量 分配内存,并初始化
解析:把常量池中的符号引用转换成直接引用
初始化:为类的静态变量赋初始值
Java 虚拟机自带的加载器包括以下几种:
启动类加载器(BootstrapClassLoader)
平台类加载器(PlatformClassLoader) JDK8:扩展类加载器(ExtensionClassLoader)
应用程序类加载器(AppClassLoader)
用户自定义的加载器:是 java.lang.ClassLoader 的子类,用户可以定制类的加载方式;只不过自定义类加载器其加载的顺序是在所有系统类加载器的最后


对象在内存中储存的布局(这里以Hotspot虚拟机为例来说明),分为:对象头、实例数据和对齐填充
对象头,包含两部分:
Mark Word:存储对象自身的运行数据,如:HashCode、GC分代年龄,锁状态标志等
类型指针:对象指向它的类元数据的指针
实例数据:真正存放对象实例数据的地方
对齐填充:这部分不一定存在,也没有什么特别含义,仅仅是占位符。因为 HotSpot 要求对象起始地址都是8字节的整数倍,如果不是,就对齐


什么是垃圾:简单说就是内存中已经不再被使用到的内存空间就是垃圾
垃圾回收算法:
可作为GC Roots的对象包括:虚拟机栈(栈帧局部变量)中引用的对象、方法区类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI引用的对象
HotSpot 使用了一组叫做 OopMap 的数据结构达到准确式GC的目的
在OopMap的协助下,JVM可以很快的做完GC Roots 枚举。但是JVM并没有为每一条指令生成一个OopMap
记录OopMap 的这些“特定位置”被称为安全点,即当前线程执行到安全点后才允许暂停进行GC
如果一段代码中,对象引用关系不会发生变化,这个区域中任何地方开始GC都是安全的,那么这个区域称为安全区域
优点:失效简单、效率高
缺点:不能解决对象之间循环引用的问题
引用计数法:给对象添加一个引用计数器,有访问就加1,引用失效就减1
根搜索算法(可达性分析法):从根(GC Roots)节点向下搜索对象节点,搜索走过的路径称为引用链,当一个对象到根之间没有连通的话,则该对象不可用
标记清除算法(Mark-Sweep):分为标记和清除两个阶段,先标记出要回收的对象,然后统一回收这些对象
在这里插入图片描述
优点:简单
缺点:
效率不高,标记和清除的效率都不高
产生大量不连续的内存碎片,从而导致在分配大对象时触发GC
垃圾收集器
串行收集器
并行收集器****ParNew收集器
新生代Parallel Scavenge 收集器
CMS收集器
CMS(Concurrent Mark and Sweep 并发标记清除)收集器分为:初始标记:只标记GC Roots 能直接关联到的对象;并发标记:进行GC Roots Tracing 的过程
重新标记:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象
并发清除:并发回收垃圾对象
在这里插入图片描述
在初始化标记和重新标记两个阶段还是会发生 Stop-the-World
使用标记清除算法,多线程并发收集的垃圾收集器
最后的重置线程,指的是清空跟收集相关的数据并重置,为下次收集做准备
优点:低停顿,并发执行
缺点:
并发执行,对 CPU 资源压力大
无法处理 在处理过程中 产生的垃圾(浮动垃圾),可能导致 FullGC
采用的标记清除算法会导致大量碎片,从而在分配大对象可能触发 FullGC
开启:-XX:UseConcMarkSweepGC:使用 ParNew + CMS + Serial Old 的收集器组合,Serial Old 将作为 CMS 出错的后备收集器
-XX:CMSInitiatingOccupancyFraction:设置 CMS 收集器在老年代空间被使用多少后触发回收,默认 80%
G1收集器
G1(Garbage-First)收集器:是一款面向服务应用的收集器,与其他收集器相比,具有以下特点:
G1 把内存划分成多个独立的区域(Region)
G1 仍采用分代思想,保留了新生代和老年代,但它们不再是物理隔离的,而是一部分Region的集合,且不需要 Region 是连续的
在这里插入图片描述
G1 能充分利用多 CPU 、多核环境硬件优势,尽量缩短 STW
G1 整体上采用标记-整理算法,局部是通过复制算法,不会产生内存碎片
G1 的停顿可预测,能明确指定在一个时间段内,消耗在垃圾收集上的时间不能超过多长时间
G1 跟踪各个 Region 里面垃圾堆的价值大小,在后台维护一个优先列表,每次根据允许的时间来回收价值最大的区域,从而保证在有限时间内的高效收集
垃圾收集:
初始标记:只标记GC Roots 能直接关联到的对象
并发标记:进行 GC Roots Tracing 的过程
最终标记:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象
筛选回收:根据时间来进行价值最大化的回收
使用和配置G1:-XX:+UseG1GC:开启G1,默认就是G1
-XX:MaxGCPauseMillis = n :最大GC停顿时间,这是个软目标,JVM将尽可能(但不保证)停顿小于这个时间
-XX:InitiatingHeapOccupancyPercent = n:堆占用了多少的时候就触发GC,默认为45
-XX:NewRatio = n:默认为2
-XX:SurvivorRatio = n:默认为8
-XX:MaxTenuringThreshold = n:新生代到老年代岁数,默认是15
-XX:ParallelGCThreads = n:并行GC的线程数,默认值会根据平台不同而不同
-XX:ConcGCThreads = n:并发 GC 使用的线程数
-XX:G1ReservePercent = n:设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险,默认值是 10%
-XX:G1HeapRegionSize = n:设置的 G1 区域的大小。值是2的幂,范围是1MB到32MB,目标是根据最小的Java堆大小划分出约2048个区域
ZGC收集器:JDK11加入的具有实验性质的低延迟收集器
ZGC的设计目标是:支持TB级内存容量,暂停时间低(<10ms),对整个程序吞吐量的影响小于15%
ZGC里面的新技术:着色指针 和 读屏障
GC性能指标:
吞吐量 = 应用代码执行的时间 / 运行的总时间
GC负荷,与吞吐量相反,是 GC 时间 / 运行的总时间
暂停时间,就是发生 Stop-the-World 的总时间
GC 频率,就是GC在一个时间段发生的次数
反应速度:就是从对象成为垃圾开始到被回收的时间
交互式应用通常希望暂停时间越少越好
JVM内存配置原则:
新生代尽可能设置大点,如果太小会导致:
对于老年代,针对响应时间优先的应用:由于老年代通常采用并发收集器,因此其大小要综合考虑并发量和并发持续时间等参数
对于老年代,针对吞吐量优先的应用:通常设置较大的新生代和较小的老年代,这样可以尽可能回收大部分短期对象,减少中期对象,而老年代尽量存放长期存活的对象
依据对象的存活周期进行分类,对象优先在新生代分配,长时间存活的对象进入老年代
根据不同代的特点,选取合适的收集算法:少量对象存活,适合复制算法;大量对象存活,适合标记清除或标记整理
如果设置小了,可能会造成内存碎片,高回收频率会导致应用暂停
如果设置大了,会需要较长的回收时间
YGC 次数更加频繁
可能导致 YGC 后的对象进入老年代,如果此时老年代满了,会触发FGC
内存间的交互操作
volatile 基本上是 JVM 提供的最轻量级的同步机制,用 volatile 修饰的变量,对所有的线程可见,即对 volatile 变量所做的写操作能立即反映到其他线程中
用 volatile 修饰的变量,在多线程环境下仍然是不安全的
volatile 修饰的变量,是禁止指令重排优化的
适合使用 valatile 的场景
运算结果不依赖变量的当前值
确保只有一个线程修改变量的值
指令重排:指的是 JVM 为了优化,在条件允许的情况下,对指令进行一定的重新排列,直接运行当前能够立即执行的后序指令,避开获取下一条指令所需数据造成的等待
线程内串行语义,不考虑多线程间的语义
不是所有的指令都能重排,比如:
写后读 a = 1;b = a;写一个变量之后,再读这个位置
写后写 a = 1;a = 2;写一个变量之后,再写这个变量
读后写 a = b;b = 1;读一个变量之后,再写这个变量
以上语句不可重排,但是 a = 1;b = 2;是可以重排的
程序顺序原则:一个线程内保证语义的串行性
volatile规则:volatile 变量的写,先发生于读
锁规则:解锁(unlock)必然发生在随后的加锁(lock)前
传递性:A 先于 B,B 先于 C,那么 A 必然先于 C
线程的 start 方法先于它的每一个动作
线程的所有操作先于线程的终结
线程中断(interrupt())先于被中断线程的代码
对象的构造函数执行结束先于 finalize() 方法
不可变是线程安全的
互斥同步(阻塞同步):synchronized、java.util.concurrent.ReentrantLock。目前这两个方法性能已经差不多了,建议优先选用 synchronized,ReentrantLock 增加了如下特性:
等待可中断:当持有锁的线程长时间不释放锁,正在等待的线程可以选择放弃等待
公平锁:多个线程等待同一个锁时,须严格按照申请锁的时间顺序来获取锁
锁绑定多个条件:一个 ReentrantLock 对象可以绑定多个 condition 对象,而 synchronized 是针对一个条件的,如果要多个,就得有多个锁
非阻塞同步:是一种基于冲突检查的乐观锁策略,通常是先操作,如果没有冲突,操作就成功了,有冲突再采取其他方式进行补偿处理
无同步方案:其实就是在多线程中,方法并不涉及共享数据,自然也就无需同步了
这是我目前知道的唯一询问方式。据了解,Scala使用Java虚拟机。我以为Jruby也是。Twitter将其中间件切换为Scala。他们可以做同样的事情并使用Jruby吗?他们是否可以从Jruby开始,而不是因为扩展问题导致他们首先从Ruby迁移到Scala?我不明白Jruby是什么吗?我假设因为Jruby可以使用Java,所以它可以扩展到Ruby不能的地方。在这种情况下,一切都归结为静态类型与动态类型吗? 最佳答案 Scala是“可扩展的”,因为语言可以通过库进行改进,使扩展看起来像是语言的一部分。这就是为什么actors看起来像
我想将我的要点(gist.github)嵌入到我的博主博客中。但正如this中所解释的那样直接提问动态View不支持javascript。从moski(如答案中所述)的博客中可以嵌入要点。如果我只想嵌入一个要点文件怎么办?例如: 最佳答案 查看moski的博客、他的描述和要点片段(gistLoader.js和gistBlogger.js),我可以假设要实现您的目标,您必须稍微编辑该代码。目前,当你添加在您帖子的底部,此脚本的作用是查找您添加到博客中的其他代码Loading....检索data-id属性,并注入(inject)所需的代
有人可以解释这个要点中发生了什么吗?我了解装饰器的概念以及此实现如何让人们创建通用装饰器,但我在几个部分(内联评论)中几乎没有迷失。如果有人能为我分解它,我将不胜感激。此外,如果这不是编写通用装饰器的最佳方式,那什么才是?我正在寻找一种装饰器,它可以装饰func(args...interface{})(interface{},error)类型的函数,而不会丢掉类型安全性。https://gist.github.com/saelo/4190b75724adc06b1c5apackagemainimport("fmt""reflect")funcDecorate(implinterface
对于我正在创建的XML文件,我有包含项目符号的数据•在xml数据中处理此项目的最佳方法是什么?它在XML编辑器中打开并且读取正常,但我无法通过SSIS导入文件,我收到关于这一点的错误。•Bullet呈现良好,但无法使用SSIS导入。 最佳答案 转义:• 关于xml-XML文件处理要点的最佳方式•,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/2454566/
我正在使用jvmcucumber并行插件并想重新运行失败的测试用例。需要在.pom文件中进行哪些更改。com.github.temyerscucumber-jvm-parallel-plugin4.2.0generateRunnersgenerate-test-sourcesgenerateRunners${basedir}/target/runnercom.xxx.stepdefscom.xxx.cucumber.hookssrc/test/resources/feature${basedir}/target/cucumberreport/jsonjsontruejsontruepa
我想通过Windows上的桌面快捷方式运行Eclipse,并使用您可以在命令行上使用的-Duser.timezone参数。我的快捷方式目标如下所示:C:\Alan\SDK\3.7.1\eclipse.exe-vm"c:\ProgramFiles\Java\jdk1.6.0_22\bin\javaw.exe"-vmargs-Xmx512m-Xmx1024M-XX:PermSize=128M-XX:MaxPermSize=256M如您所见,我使用-X..参数增加了内存空间。无论出于何种原因,我都无法在-vmargs条目后键入-Duser.timezone=Europe/Dublin。不可能
我正在编写一个(批处理文件或VBScript)来很好地关闭Windows服务器上所有正在运行的WebSphereJVM,但需要一些文本处理方面的帮助。我希望脚本运行并解析“serverstatus”命令的输出以获取框中的ApplicationServers名称并将匹配项(带回车符)存储在用于脚本其余部分的变量。示例命令输出:C:\WebSphere\AppServer\bin>serverstatus-allADMU0116I:ToolinformationisbeingloggedinfileC:\WebSphere\AppServer\profiles\MySrv01\logs\s
有点奇怪的问题,但我在使用mklink创建符号链接(symboliclink)时遇到了问题在Windows7上。由于使用cmd.exe时存在260个字符的限制,我正在做一些奇怪的事情。通过使用Process在我的Java源代码中创建符号链接(symboliclink).由于我不能完全解释它,这里是代码:importjava.io.BufferedInputStream;importjava.io.BufferedReader;importjava.util.ArrayList;importjava.util.List;importjava.util.Map;importjava.uti
我有一个使用嵌入式Jetty(版本9.3.6.v20151106)和JDK8u65的应用程序。当我在Mac或Linux上使用这个应用程序时,我没有任何困难。但是,在Windows上,Jetty不会启动并且应用程序会永久挂起。我在进程上运行了一个jstack命令并隔离了阻止服务器启动的线程。java.lang.Thread.State:WAITING(parking)atsun.misc.Unsafe.park(NativeMethod)-parkingtowaitfor(ajava.util.concurrent.Semaphore$NonfairSync)atjava.util.co
正如我们所知,JVM实现是特定于操作系统的(Windows/Linux/Solaris等)。我想更深入地研究一下,即我们是否针对不同的Windows操作系统版本有不同的JVM实现?例子:JVM-Implementation-For-Win-XP和JVM-Implementation-For-Win-8一样吗?? 最佳答案 IsJVM-Implementation-For-Win-XPsameasJVM-Implementation-For-Win-8??是也不是。它们大多使用相同的代码库,但各处存在一些差异。例如,WindowsXP