一、class文件结构
二、jclasslib使用
IDEA安装jclasslib插件
执行“build project”,点击view>>Show Bytecode with JclassLib,如图
java代码示例:
public class JclasslibHello {
public int add(){
int a = -1;
int b = 3;
return a+b;
}
}
对应bytecode如下:
0 iconst_m1 //常量-1加载到操作数栈
1 istore_1 //将-1从操作数栈存储到局部变量表第2个位置
2 iconst_3 //常量3加载到操作数栈
3 istore_2 //将3从操作数栈存储到局部变量表第3个位置
4 iload_1 //加载局部变量第1个变量压入操作数栈
5 iload_2 //加载局部变量第2个变量压入操作数栈
6 iadd //操作数栈中两个变量相加,并将结果压入操作数栈顶
7 ireturn //返回
三、编译执行与解释执行
解释器:是一种电脑程序,能够把高级编程语言一行一行直接翻译运行。解释器不会一次把整个程序翻译出来,每次运行程序时都要先转成另一种语言再作运行,因此解释器的程序运行速度比较缓慢。
Java编译器:将Java源文件(.java文件)编译成字节码文件(.class文件,是特殊的二进制文件,二进制字节码文件)
JIT编译器(注意与Java解释器区分):JIT编译器是JRE的一部分。原本的Java程序都是要经过解释执行的,其执行速度肯定比可执行的二进制字节码程序慢。为了提高执行速度,引入了JIT。在运行时,JIT会把翻译过来的机器码保存起来,以备下次使用。而如果JIT对每条字节码都进行编译,则会负担过重,所以,JIT只会对经常执行的字节码进行编译,如循环,高频度使用的方法等。它会以整个方法为单位,一次性将整个方法的字节码编译为本地机器码,然后直接运行编译后的机器码。
混合执行:解释执行、编译执行
多次被调用的方法,或者多次被调用的循环,热点代码进行编译。
四、类加载过程
一、loading加载
把class文件加载到内存中
二、linking链接
1、verfication验证class文件的正确性
2、preparation准备,把class文件中静态变量赋默认值,不是赋初始值,比如static int a=3,这里的a是赋0
3、resolution解析,把class文件常量池里面用到的符号引用转换为直接内存地址
三、initializing初始化,静态变量这时赋初始值
五、类加载器
双亲委派
双亲委派就是孩子向父亲方向,然后父亲向孩子方向的双亲委派过程。
其中一个类加载器收到类加载的请求,将该请求委托给父类的加载器去加载,如果父类加载器还存在父类加载器,就进行进一步委托,如此递归,直至到达顶层的类加载器。如果父类加载器能加载,则直接返回,如果加载不了,就交给子类加载器加载。
好处:为了避免重复加载,导致核心类被篡改。
六、JVM内存模型
1、程序计数器
当前线程所执行的字节码的行号指示器,线程私有。
java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
2、虚拟机栈
没执行一个java方法,都会去创建一个栈帧,用于存放局部变量表,操作栈等信息,线程私有。
如果线程请求的栈深度大于虚拟机允许的深度,就抛出StackOverflowError异常。
如果申请的线程超过剩余可用线程,就会抛出OutOfMemoryError。
3、本地方法栈
和虚拟机栈类似,只是本地方法栈是为本地native方法服务。线程私有。
本地方法栈和虚拟机栈一样会出现StackOverflowError和OutOfMemoryError。
4、堆
几乎所有的对象和数组都是存放在堆内存中。线程共享
如果堆没有内存完成实力分配,并且无法拓展时,将抛OutOfMemoryError。
5、方法区
已经被虚拟机加载的类信息、常量、静态变量等数据。线程共享。
方法区没法满足内存分配时,会出现OutOfMemoryError。
6、运行时常量池
运行时常量池属于方法区里面的一部分,用于存放编译期生产的各种字面量和符号引用。
受方法区的内存限制,也会出现OutOfMemoryError。
七、内存溢出和内存泄露
内存溢出是指程序在申请内存时,没有足够的内存供其使用。
内存泄露是指本来不需要再使用本该回收的,但是另外一个在使用的对象持有他的引用。导致不能回收。
八、垃圾收集算法和垃圾收集器
垃圾收集器算法
标记清除、复制、标记整理、分代回收
CMS垃圾收集器
步骤:1、初始标记;2、并发标记;3、重新标记;4、并发清理
初始标记和重新标记会STW
G1垃圾收集器
步骤:1、初始标记;2、并发标记;3、最终标记;4、筛选回收
初始标记、最终标记、筛选回收会STW。
G1相比CMS更注重用户体验,也就是减少STW的时间。
1、初始标记只是标记GC Roots能直接关联到的对象。
2、并发标记是从GC Roots开始对堆中对象进行可达性分析,找出存活对象。耗时较长,但是和用户线程并发运行。
3、为了修正在并发标记期间因用户线程执行导致标记产生变动的一些记录,虚拟机将变化记录在线程的Remembered Set Logs里面。在最终标记的时候,会将Remembered Set Logs合并到Remembered Set里面。这个阶段需要STW,但是是并行执行的。
4、筛选回收阶段,G1使用了独立去Region概念,筛选回收的时候,会对各个Region的回收价值和成本排序,根据用户所期望GC时长来定制回收计划。