使用软件模拟java字节码的指令集
1. java xxx 执行运行命令
2. 装载配置(jvm.cnf)
3. 寻找dll(jvm.dll为java虚拟机的主要实现)
4. 初始化jvm(JNIEnv为jvm接口,findClass通过它实现)
5. 找到mian方法并运行
每个线程都有一个独立的pc寄存器
保存装载的类信息
应用系统对象保存、所有线程共享堆、gc的主要工作空间、堆也是分代的(分代gc)
线程私有,栈由一些列帧组成、帧保存一个方法的局部变量、操作数栈、常量池指针,每一个方法调用创建一个帧、并压栈
一个线程修改了变量,其他线程可以立即知道
保证可见性方式
volatile
synchronized unlock之前,将变量值写会主存
final 一旦初始化完成,其他线程可见
本线程内,操作都是有序的
在线程外观察,操作都是无序的(指令重排或者主存延时)
保证线程内按顺序执行的结果完全相同
指令重排原则:
顺序原则:一个线程内保证语义的串行性
volatile规则:volatile变量的写,先发生于读
锁规则:解锁必然发生于随后的加锁前
传递性:a先于b,b先于c,则a必然先于c
线程的start先于他的每一个动作
线程的所有操作先于线程的终结
线程的中断先于被中断的线程的代码
对象的构造函数的执行结束先于finalize()方法
-verbose:gc (打开gc跟踪日志)
-XX:+PrintGC 打印gc简要信息
-XX:+PrintGCDetails 打印gc详细信息
-XX:+PrintGCTimeStamps 打印gc发生的时间戳
-Xloggc:log/gc.log 指定gc的log文件的位置
-XX:+PrintHeapAtGC 每一个gc后,打印堆信息
-XX:+TraceClassLoading 监控类的加载
-XX:+PrintClassHistogram 按下ctrl+break后,打印类的信息(分别显示:序号、实例数量、总大小、类型)
-Xmx -Xms 指定最大堆和最小堆空间
-Xmx20m 最大可用20m
-Xms5m 初始化占用5m
-Xmn 设置新生代的大小的绝对值
-XX:NewRatio 设置新生代的大小比例(设置4 表示 新生代:老年代=1:4,即年轻代占堆的1/5) 官方推荐占3/8
-XX:SurvivorRatio 设置两个survivor和Eden的比(设置8 表示s:e=2:8,即一个survivor占年轻代的1/10) 官方推荐占1/10
-XX:HeapDumpOnOutOfMenoryError 发生内存溢出时导出堆到文件
-XX:+HeapDumpPath 设置发生内存溢出导出文件的路径
-XX:OnOutOfMenoryError 在发生内存溢出时执行一个脚本
-XX:PermSize 设置永久区的大小
-XX:MaxPermSize 设置永久区的最大空间大小
如果堆空间未使用完,发生了内存溢出,则有可能是永久代内存溢出
通常只有几百k
、决定了函数调用的深度
、每个线程都有独立的栈空间
、局部变量、参数分配在栈上
-Xss 分配栈空间的大小(e:-Xss128k)
GC:垃圾回收,GC的回收目标是堆空间和永久区
通过引用计算来回收垃圾
为每个对象设计一个引用计数器,如果有对象引用该对象,则计数加1,引用释放,则计数减1
比较影响性能,始终都伴随着应用加和减
很难处理循环引用
java中没有被使用
现代垃圾回收算法的思想基础
分为标记阶段(通过根节点,标记所有从根节点开始的可达对象,未被标记的对象即是垃圾对象)和清除阶段(清除所有未被标记的对象)
适用于存活对象较多的场合,如老年代
从根节点开始,对所有可达对象做一次标记,之后将所有存活对象整理到内存的一端,然后清理边界外的所有空间
是一种相对高效的算法
不适用于存活对象较多的场合
将原有空间分为两块,每次只使用其中一块,垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存中,之后清除正在使用块的所有对象,然后交换两个内存角色
比较浪费空间
依据对象的存活周期,存活时间短的归为新生代,存活时间长的对象为老年代
根据不同代的特点,选取合适的收集算法(少量对象存活,适合复制算法)(大量对象存活,适合标记清除和标记整理)
可触及的:从根节点可以触及到这个对象
可复活的:一旦所有引用被释放,就是可复活状态
在调用finalize()中可能复活的对象
不可触及的:执行finalize()之后会进入不可触及状态
不可触及的对象不可能复活
可以被垃圾回收器回收
finalize()方法只会被调用一次,在第一次执行gc调用,之后的gc操作不会调用finalize()
避免使用finalize(),操作不慎可能导致错误
gc的调用不确定
可以使用try-catch-finally处理资源的释放
栈中引用的对象
方法区中静态成员或者常量引用的对象
JNI(java native 方法)方法栈中引用的对象
java中一种全局暂停的现象
全局停顿,所有java代码停止,native代码可执行,但不能和jvm交互
多半由于gc引起(dump线程,死锁检查,堆dump也可能引起)
可能会导致(长时间停止服务,没有响应 高可用系统中可能会引起主备切换,危害生产环境)
最古老,最稳定,效率高,可能会产生比较长的停顿
-XX:+UseSerialGC //串行回收器
//新生代,老年代会使用串行回收
//新生代使用的是复制算法
//老年代使用的是标记-整理算法
ParNew收集器
-XX:+UseParNewGC //并行回收器
//新生代并行,老年代串行
//Serial收集器新生代的并行版本
//使用复制算法
//多线程性能比较好,需要多核支持
-XX:ParallelGCThreads //限制线程数量
parallel收集器
//类似ParNew
//新生代使用复制算法
//老年代使用标记-整理算法
//更加关注吞吐量
//串行收集器在新生代以及老年代的并行化
-XX:+UseParallelGC //新生代并行,老年代串行
-XX:+UseParallelOldGC //新生代并行,老年代并行
并行收集器其他参数
-XX:MaxGCPauseMills //最大停顿时间,单位毫秒,GC尽量保证回收时间不超过该时间
-XX:GCTimeRatio //0-100的取值范围,垃圾回收时间占总时间的比值,默认99,即允许1%时间做GC
//这两个参数是矛盾的,吞吐量和停顿时间不可能同时调优
CMS收集器
-XX:UseConcMarkSweepGC //CMS收集器,与应用程序一起并行执行,减少停顿(尽可能缩小,不能完全消除),降低吞吐量
//Concurrent Mark Sweep 并发标记清除
//使用标记-清除算法
//与标记-整理相比,并发阶段会降低吞吐量
//是老年代收集器(新生代使用PerNew)
//运行过程:
//初始标记【停顿】(标记根可直接关联到的对象,速度快)
//并发标记【并行】(主要标记过程,标记全部对象)
//重新标记【停顿】(由于并发标记时,用户线程依然运行,因此正式清理前再做修正)
//并发清除【并行】(基于标记结果,直接清理对象)
//并发重制【并行】(为下次回收做准备)
//特点:
//尽可能降低停顿
//会影响系统整体吞吐量和性能
//清理不彻底(和用户线程一起执行,会产生新的垃圾)
//不能再空间即将满的时候再清理(如果预留空间不足,会引起concurrent mode failure,如果出现该错误,会使用[串行收集器]作为后备)
-XX:CMSInitiatingOccupancyFraction //设置触发GC的阈值,当堆空间占用的比值达到该阈值即触发
-XX:+UseCMSCompactAtFullCollection //full gc之后进行一次内存整理,整理期间停顿
-XX:CMSFullGCsBeforeCompaction //设置进行几次full gc之后进行一次碎片整理
-XX:ParallelCMSThreads //设定CMS的线程数,一般情况约为CPU数量
-XX:+CMSClassUnoladingEnabled //允许对类的元数据进行回收
-XX:CMSInitiatingPermOccupancyFraction //当永久区占用率达到该百分比值时,启用CMS收集器
-XX:UseCMSInitiatingOccupancyOnly //表示只在达到阈值的时候,才进行CMS回收
G1收集器
-XX:+UseG1GC //G1收集器
//G1(Garbage-First)是面向服务端应用的垃圾收集器。
//特点:
//并行与并发,G1收集器可以通过并发的方式让Java程序继续执行
//分代收集,G1可以不需要其他收集器配合就能独立管理整个GC堆,采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
//空间整合,G1从整体来看是基于 标记—整理算法 实现的收集器,从局部来看是基于 复制算法 实现的,这两种算法都意味着G1运作期间不会产生内存空间碎片。
//可预测的停顿,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。
//使用G1收集器时,Java堆的内存布局就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。
-XX:G1HeapRegionSize //设置region的大小
//G1收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region(这也就是Garbage-First名称的来由)。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限的时间内可以获取尽可能高的收集效率。
//执行过程:
//初始标记【停顿】(标记根可以直接关联到的对象,并修改TAMS[next top at mark start]的值),让下一阶段用户程序并发执行时,能在正确的可用Region中创建新对象
//并发标记【并行】(标记所有对象,与用户程序并发执行)
//最终标记【停顿】(修正在并发标记期间因用户程序继续运行而导致的标记变动)
//筛选回收【停顿】(对各个Region的回收价值和成本进行排序,根据用户期望的停顿时间制定回收计划,这个阶段可以做到并行执行,但是只回收一部分Regina,时间是用户可控制的,停顿可以大幅提高收集效率)
-XX:InitiatingHeapOccupancyPercent //堆内存占用达到该百分比值时启动并发GC
-XX:MaxTenuringThreshold //提升老年代的最大临界值,默认为15
-XX:G1ReservePercent //预留多少内存,防止晋升失败的情况,默认值是10
加载-->链接(验证,准备,解析)-->初始化
加载:读取二进制文件,转换为方法区数据结构,在java堆生成对应的Class对象
链接:验证–>验证class文件格式(class文件以0xcafebabe开头),版本号是否合理,验证元数据(语法语义校验),字节码检查,符号引用验证(访问方法的权限,属性类是否存在等)
准备-->分配内存,并为类设置初始值
解析-->将符号引用替换为直接引用(符号替换为真实内存地址)
初始化:执行类构造器(ClassInit),赋值static变量(final变量除外)和执行静态代码块,子类初始化之前保证父类先初始化,ClassInit是线程安全的
抽象类,负责类装载过程中的加载阶段,将java字节码装载到jvm中,可以定制,满足不同的字节码流获取方式
重要方法:loadClass(载入并返回一个Class)、defineClass(定义一个类,不公开调用)、findClass(loadClass回调该方法,自定义时推荐重载该方法)、findLoadedClass(寻找已经加载的类)
几种类加载器:启动加载(Bootstrap ClassLoader)、扩展加载(Extension ClassLoader)、应用加载(App ClassLoader)、自定义加载(Custom ClassLoader)
除了Bootstrap外每个ClassLoader都有一个parents父类(双亲委派)
默认加载方式:自底向上检查是否已加载(Bootstrap为最上方),然后自顶向下加载类
-Xbootclasspath/a:D:/tmp/clz_dir
//指定bootstrap加载器的附加加载目录
//上下文加载器,是一个角色,用以解决顶层ClassLoader无法访问底层ClassLoader的类的问题,原理是在顶层ClassLoader中传入底层ClassLoader的实例
Thread.setContextClassLoader()
双亲模式是默认的,但不是必须这么做,如tomcat的WebAPPClassLoader就会先加载自己的class,找不到再委托parent,而不是先委托parent加载
uptime(linux)
:系统运行时间,连接数,系统在1,5,15分钟内的平均负载
top(linux)
:cpu,内存使用情况
vmstat(linux)
:统计cpu,内存,swap(上下文切换),io等情况
pidstat(linux)
:(需要安装 sysstat)细致观察进程,监控cpu,io,mem,可以显示进程中的线程信息(-t参数)
perfmon(win)
:windows性能监控工具
process Explorer(win)
:系统性能监控,查看线程运行情况
pslist(win cmd)
:(需要安装)显示java程序的运行情况
//java 自带的监控工具
jdb java调试工具
jhat 分析java堆
jinfo 查看正在运行的java程序的参数,支持运行时修改部分参数,-flag (打印指定jvm参数值)
jmap 生成java程序的堆快照和对象的统计信息,-histo,-dump
jps 列出java进程,-q(只输出pid),-m(输出主函数参数),-l(主函数完整路径),-v(显示传递给jvm的参数)
jrunscript 一个命令行脚本外壳
jstack 打印线程dump,-l(打印锁信息),-m(打印java和native的帧信息),-F(强制dump,当jstack没有响应时使用)
jstat 查看堆内存各部分的使用量,以及加载类的数量
jstatd 用于监控基于HotSpot的JVM中资源的创建及销毁
jConsole 图形化工具,查看java程序运行情况,监控堆信息,永久区使用情况,类加载情况等
jvisualvm 多合一的可视化监控工具,可以分析堆信息
jvm内存中的内存区间:堆,永久区,线程栈,直接内存
堆溢出
:占用大量堆空间,直接溢出(解决:增大堆空间或及时释放内存)
永久区溢出
:大量的类信息导致(增大perm区,允许class回收)
栈溢出
:操作系统无法为线程分配栈空间,即超过一定的线程数(每个线程独立分配一块空间),导致内存不足(解决:减少堆内存,减少线程栈大小)
直接内存溢出
:操作系统无法获得足够空间,外部内存超过系统可用内存(解决:减少堆内存,有意触发gc)
eclipse下的软件,可在eclipse官网下载
浅堆
:一个对象结构所占用的内存大小,与对象内容无关,只与对象结构有关
深堆
:一个对象被gc回收后,可以真实释放的内存大小,即可以通过对象访问到的所有对象的浅堆之和
使用锁维护程序的串行访问和安全性
对象头标记,32位
描述对象的hash,锁信息,垃圾回收标记,年龄
大部分情况是没有竞争的,所以可以通过偏向来提高性能,锁会偏向当前已经占有锁的线程,将对象头的mark标记置为偏向,并写入线程id,只要没有竞争,获得偏向锁的线程在将来进入同步块时不需要做同步,当其他线程请求相同的锁时,偏向模式结束,在竞争激烈的场合,偏向锁会增加系统负担
-XX:+UseBiasedLocking //1.6之后默认启用
-XX:BiasedLockingStartupDelay //设置偏向锁在程序启动后的延迟时间,偏向锁在系统启动不会立马启用
BasicObjectLock
轻量级锁是一种快速的锁定方法,如果对象没有被锁定,将对象头的mark指针保存到锁对象中,将对象头设置为指向锁的指针(在线程栈中),如果存在竞争,则升级为重量级锁
当竞争存在时,如果线程可以很快获得锁,那么可以不在系统层挂起线程,让线程做几个空操作,1.7之后为默认启用,如果同步块很长,自旋失败,会降低系统性能,如果同步块很短,自旋成功,会节省系统切换时间
减少锁的持有时间
:缩小同步块,尽量不同步整个方法
减小锁粒度
:将大对象拆成小对象,如ConcurrentHashMap,将table分为多个segment进行操作
锁分离
:根据功能进行锁分离,如读写锁
锁粗化
:对统一锁多次请求,同步,释放,可以将锁放大化,增加锁范围
锁消除
:如果发现不可能被共享的对象,则可以消除这些对象的锁操作(-XX:+DoEscapeAnalysis(逃逸分析) -XX:+EliminateLocks(粗化锁区域))
无锁
:无锁是乐观的操作,再应用层面判断多线程的干扰,如果有干扰,则重试,无锁的实现(cas(比较交换指令)),如atomic包下的类
各种jvm语言都可以编译成class文件,运行在jvm上,并不是只有java语言,即java语言和jvm是两个分离的部分
u1/u2/u4表示无符号整型以及占用的字节数,文件结构如下
类型 | 名称 | 数量 | 说明 |
---|---|---|---|
u4 | magic | 1 | 魔数 |
u2 | minor_version | 1 | 小版本 |
u2 | major_version | 1 | 大版本 |
u2 | const_pool_count | 1 | 常量数 |
cp_info | const_pool | const_pool_count-1 | 常量 |
u2 | access_flag | 1 | 访问修饰符 |
u2 | this_class | 1 | 当前类,指向常量池的Class |
u2 | super_class | 1 | 父类,指向常量池的Class |
u2 | interface_count | 1 | 接口数量 |
u2 | interfaces | interface_count | 接口,指向常量池的Class |
u2 | field_count | 1 | 字段数量 |
field_info | fileds | field_count | 字段 |
u2 | method_count | 1 | 方法数量 |
method_info | methods | method_count | 方法 |
u2 | attr_count | 1 | 属性数量 |
attr_info | attrs | attr_count | 属性 |
魔数magic
:class文件魔数为 0xCAFEBABE
minor.major
:java编译版本
常量池
:支持的常用类型 (1)指向utf8的索引 (2)指向class的索引 (3)指向NameAndType的索引
描述 | 类型 | 结构 |
---|---|---|
utf-8编码的Unicode字符串 | utf8 | tag 1,length u2,byte len |
int类型的字面值 | Integer | tag 3,byte u4 |
float类型的字面值 | Float | tag 4 |
long类型的字面值 | Long | tag 5 |
double类型的字面值 | Double | tag 6 |
对一个类或接口的符号引用 | Class | tag 7,name_index u2(1) |
string类型字面值的引用 | String | tag 8,string_index u2(1) |
对一个字段的符号引用 | Fieldref | tag 9,class_index u2(2),nameandtype_index u2(3) |
对一个类中方法的符号引用 | Methodref | tag 10,class_index u2(2),nameandtype_index u2(3) |
对一个接口中方法的符号引用 | InterfaceMethodref | tag 11,class_index u2(2),nameandtype_index u2(3) |
对一个字段或方法的部分符号引用 | NameAndType | tag 12,name_index u2(1),desc_index u2(1) |
类的标识符
:
标识 | value | 说明 |
---|---|---|
ACC_PUBLIC | 0x0001 | public |
ACC_FINAL | 0x0010 | final 不能被继承 |
ACC_SUPER | 0x0020 | 是否允许使用invokespecial指令,1.2后为true |
ACC_INTERFACE | 0x0200 | 是否为接口 |
ACC_ABSTRACT | 0x0400 | 抽象类 |
ACC_SYNTHETIC | 0x1000 | 不是由用户代码生成,运行时生成,没有源码 |
ACC_ANNOTATION | 0x2000 | 是否为注解 |
ACC_ENUM | 0x4000 | 是否是枚举 |
字段标识符
:access_flag
标识 | value | 描述 |
---|---|---|
ACC_PUBLIC | 0x0001 | public |
ACC_PRIVATE | 0x0002 | private |
ACC_PROTECTED | 0x0004 | protected |
ACC_STATIC | 0x0008 | static |
ACC_FINAL | 0x0010 | final |
ACC_VOLATILE | 0x0040 | volatile |
ACC_TRANSIENT | 0x0080 | transient |
ACC_SYNTHETIC | 0x1000 | synthetic;没有源码,编译器生成 |
ACC_ENUM | 0x4000 | enum枚举类型 |
字段
:常量池引用,表示字段的名字 name_index u2,descriptor_index u2
标识 | 类型 |
---|---|
B | byte |
C | char |
D | double |
F | float |
I | int |
J | long |
S | short |
Z | boolean |
V | void |
L | 对象 – Ljava/lang/Object |
[ | 数组 – [Ljava/lang/String |
方法标识符
:指向常量池的索引,方法描述 (args)return 如: (I)V 为 参数为int返回值空,name_index u2,descriptor_index u2
标识 | value | 描述 |
---|---|---|
ACC_PUBLIC | 0x0001 | public |
ACC_PRIVATE | 0x0002 | private |
ACC_PROTECTED | 0x0004 | protected |
ACC_STATIC | 0x0008 | static |
ACC_FINAL | 0x0010 | final |
ACC_SYNCHRONIZED | 0x0020 | synchronized |
ACC_BRIDGE | 0x0040 | 编译器产生,桥接方法 |
ACC_VARARGS | 0x0080 | 可变参数 |
ACC_NATIVE | 0x0100 | native |
ACC_ABSTRACT | 0x0400 | abstract |
ACC_STRICT | 0x0800 | strictfp |
ACC_SYNTHETIC | 0x1000 | 不在源码中,编译器生成 |
文件属性
:在field和method中,可以有若干个attr,类文件也有attr用于描述一些额外的信息,attr本身也可以包含其他的attr
attribute_name_index u2 名字,指向常量池utf8
attribute_length u4 长度
info【attribute_length】 u1 内容
名称 | 使用者 | 说明 |
---|---|---|
Deprecated | field method | 字段、方法、类被废弃,attribute_length=0 |
ConstantValue | field | final常量,attribute_length=2,constantvalue_index u2指向常量池 |
Code | method | 方法的字节码和其他数据 |
Exceptions | method | 方法的异常 |
LineNumberTable | Code_Attribute | 方法行号和字节码映射 |
LocalVaribleTable | Code_Attribute | 方法局部变量表描述 |
SourceFile | Class file | 源文件名,attribute_length=2 |
Synthetic | field method | 编译器产生的方法或字段 |
feild_info
–access_flags u2
–name_index u2
–descriptor_index u2
–attributes_count u2
–attribute_info attributes [attributes_count];
method_info
–access_flags u2
–name_index u2
–descriptor_index u2
–attributes_count u2
–attribute_info attributes [attributes_count];
sourceFile
–attribute_name_index u2
–attribute_length u4(固定为2)
–soucefile_index u2(UTF-8常量索引)
exception
–attribute_name_index u2
–attribute_length u4
–number_of_exceptions u2
–exception_index_table [number_of_exceptions] u2(指向Constant_Class的索引)
LocalVariableTable
LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
{
u2 start_pc;
u2 length;
u2 name_index;
u2 descriptor_index;
u2 index;
} local_variable_table [local_variable_table_length];
}
LineNumberTable
LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{
u2 start_pc;
u2 line_number;
} line_number_table [line_number_table_length];
}
code
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code [code_length];
u2 exception_table_length;
{
u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table [exception_table_length];
u2 attributes_count;
attribute_info attributes [attributes_count];
}
javap //JDK自带的反汇编器
jit:just in time 将执行比较频繁的代码编译成机器码,-XX:CompileThreshold=100(设定jit阈值),-XX:+PrintCompilation(打印编译信息)
(1)dump:在特定时刻,将整个储存装置或储存装置之某部分的内容记录在另一储存装置中
(2)新生代GC(Minor GC):指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快
(3)老年代GC(Major GC / Full GC):指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。
(4)吞吐量:吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)。
(5)Region:G1默认把堆内存分为1024个分区,后续垃圾收集的单位都是以Region为单位的。Region是实现G1算法的基础,每个Region的大小相等,通过-XX:G1HeapRegionSize参数可以设置Region的大小。
同类文章 https://www.cnblogs.com/yxwkf/p/5222589.html