jvm的学习一_lfffffq、的博客-爱代码爱编程
学习JVM(一)
一、java字节码技术
1.什么是字节码
Java bytecode 由单字节(byte)的指令组成,理论上最多支持 256 个操作码(opcode)。 实际上 Java 只使用了200左右的操作码, 还有一些操作码则保留给调试操作。Java的字节码技术,就是java程序具有可移植性和适应所有操作系统的根本原因。因为不管是什么样的操作系统最后计算机解析的都是java的字节码,所以不会受到影响。
根据指令的性质,主要分为四个大类:
- 栈操作指令,包括与局部变量交互的指令 2. 程序流程控制指令
- 对象操作指令,包括方法调用指令
- 算术运算以及类型转换指令
2.生成字节码
编译:javac demo/jvm0104/HelloByteCode.java
查看字节码:javap -c demo.jvm0104.HelloByteCode
结果如下:
3.字节码的运行时结构
JVM 是一台基于栈的计算机器。 每个线程都有一个独属于自己的线程栈(JVM Stack),用于存储栈帧(Frame)。每一次方法调用,JVM 都会自动创建一个栈帧。 栈帧由操作数栈, 局部变量数组以及一个 Class 引用组成。 Class 引用 指向当前方法在运行时常量池中对应的 Class。
这是一个动态的例子,按照字节码顺序 常量1进入局部变量区0,常量2进入局部变量区1, 然后加载0,1进入栈进行相加操作 然后与常量5进行相乘,值进入局部变量区2,结束返回。
二、 jvm的类加载器
1.类的生命周期
类的加载周期分为七步,加载过程分为五个步骤。在加载一个类
- 加载(Loading):找 Class 文件
- 验证(Verification):验证格式、依赖
- 准备(Preparation):静态字段、方法表
- 解析(Resolution):符号解析为引用
- 初始化(Initialization):构造器、静态变 量赋值、静态代码块
- 使用(Using)
- 卸载(Unloading)
2.类的加载时期
- 当虚拟机启动时,初始化用户指定的主类,就是启动执行的 main 方法所在的类;
- 当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类,就是 new 一个类的时候要初始化;
- 当遇到调用静态方法的指令时,初始化该静态方法所在的类;
- 当遇到访问静态字段的指令时,初始化该静态字段所在的类;
- 子类的初始化会触发父类的初始化;
- 如果一个接口定义了 default 方法,那么直接实现或者间接实现该接口的类的初始化, 会触发该接口的初始化;
- 使用反射 API 对某个类进行反射调用时,初始化这个类,其实跟前面一样,反射调用 要么是已经有实例了,要么是静态方法,都需要初始化;
- 当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类。
3.类加载器
java的类加载器使用的双亲委派机制,即先有父类加载器进行加载,父类无法加载再由自己加载。这样的机制是为了避免类的重复加载。
三类加载器:
- 启动类加载器(BootstrapClassLoader) 加载根目录下的jar
- 扩展类加载器(ExtClassLoader) 在exc目录下的jar包
- 应用类加载器(AppClassLoader)
三、jvm的内存模型
1.jvm内存结构
1.每个线程都只能访问自己的线程栈。
2.每个线程都不能访问(看不见)其他线程的局部变量。
3.所有原生类型的局部变量都存储在线程栈中,因此对其他线程是不可见的。
4.线程可以将一个原生变量值的副本传给另一个线程,但不能共享原生局部变量本身。
5.堆内存中包含了 Java 代码中创建的所有对象,不管是哪个线程创建的。 其中也涵盖了包装类型 (例如 Byte,Integer,Long 等)。
6.不管是创建一个对象并将其赋值给局部变量, 还是赋值给另一个对象的成员变量, 创建的对象都 会被保存到堆内存中。
2.jvm的整体结构
1.jvm内存的整体结构
1.每启动一个线程,JVM 就会在栈空间栈分 配对应的 线程栈, 比如 1MB 的空间(- Xss1m)。
2.线程栈也叫做 Java 方法栈。 如果使用了 JNI 方法,则会分配一个单独的本地方法栈 (Native Stack)。
3.线程执行过程中,一般会有多个方法组成调 用栈(Stack Trace), 比如 A 调用 B,B 调用 C。。。每执行到一个方法,就会创建对应的栈帧(Frame)。
2.jvm栈内存结构
栈帧是一个逻辑上的概念,具体的大小在一个方法编写完成后基本上就能确定。比如返回值 需要有一个空间存放吧,每个 局部变量都需要对应的地址空间,此外还 有给指令使用的 操作数栈,以及 class 指 针(标识这个栈帧对应的是哪个类的方法, 指向非堆里面的 Class 对象)。
3,jvm的堆内存结构
堆内存是所有线程共用的内存空间,JVM 将Heap 内存分为年轻代(Young generation)和 老年代(Old generation, 也叫 Tenured)两部分。
一、年轻代还划分为 3 个内存池,新生代(Eden space)和存活区(Survivor space), 在大部分 GC 算法中有 2 个存活区(S0, S1),在我们可 以观察到的任何时刻,S0 和 S1 总有一个是空的, 但一般较小,也不浪费多少空间。Non-Heap 本质上还是 Heap,只是一般不归 GC 管理,里面划分为 3 个内存池。
二、Metaspace, 以前叫持久代(永久代, Permanent generation), Java8 换了个名字叫 Metaspace.CCS, Compressed Class Space, 存放 class 信 息的,和 Metaspace 有交叉。Code Cache, 存放 JIT 编译器编译后的本地机器 代码。
四、jvm的启动参数
以-开头为标准参数,所有的 JVM 都要实现这些参 数,并且向后兼容。
-D 设置系统属性。
以 -X 开头为非标准参数, 基本都是传给 JVM 的, 默认 JVM 实现这些参数的功能,但是并不保证所 有 JVM 实现都满足,且不保证向后兼容。 可以使 用 java -X 命令来查看当前 JVM 支持的非标准参 数。
以 –XX:开头为非稳定参数, 专门用于控制 JVM 的行为,跟具体的 JVM 实现有关,随时可能会在 下个版本取消。
-XX:±Flags 形式, ± 是对布尔值进行开关。 -XX:key=value 形式, 指定某个选项的值。
1.jvm启动参数–堆内存
1.Xmx, 指定最大堆内存。 如 -Xmx4g. 这只是限制了 Heap 部分的最大值为4g。这个内存不包括栈内存,也不包括堆外使用的内存。
2.-Xms, 指定堆内存空间的初始大小。 如 -Xms4g。 而且指定的内存大小,并 不是操作系统实际分配的初始值,而是GC先规划好,用到才分配。 专用服务 器上需要保持 –Xms 和 –Xmx 一致,否则应用刚启动可能就有好几个 FullGC。 当两者配置不一致时,堆内存扩容可能会导致性能抖动。
3.-Xmn, 等价于 -XX:NewSize,使用 G1 垃圾收集器 不应该 设置该选项,在其 他的某些业务场景下可以设置。官方建议设置为 -Xmx 的 1/2 ~ 1/4.
4.-XX:MaxPermSize=size, 这是 JDK1.7 之前使用的。Java8 默认允许的 Meta空间无限大,此参数无效。
5.-XX:MaxMetaspaceSize=size, Java8 默认不限制 Meta 空间, 一般不允许设 置该选项。
6.-XX:MaxDirectMemorySize=size,系统可以使用的最大堆外内存,这个参 数跟 -Dsun.nio.MaxDirectMemorySize 效果相同。
7.-Xss, 设置每个线程栈的字节数。 例如 -Xss1m 指定线程栈为 1MB,与- XX:ThreadStackSize=1m 等价
2.jvm启动参数–GC相关
1.-XX:+UseG1GC:使用 G1 垃圾回收器
2.-XX:+UseConcMarkSweepGC:使用 CMS 垃圾回收器
3 -XX:+UseSerialGC:使用串行垃圾回收器
- -XX:+UseParallelGC:使用并行垃圾回收器
3.jvm启动参数–分析诊断
1.-XX:±HeapDumpOnOutOfMemoryError 选项, 当 OutOfMemoryError 产生,即内存溢出(堆内存或持久代)时,自动 Dump 堆内存。
示例用法: java -XX:+HeapDumpOnOutOfMemoryError -Xmx256m ConsumeHeap
2.-XX:HeapDumpPath 选项, 与 HeapDumpOnOutOfMemoryError 搭配使用, 指定内存溢出时 Dump 文件的目录。如果没有指定则默认为启动 Java 程序的工作目录。
示例用法: java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/ ConsumeHeap
自动 Dump 的 hprof 文件会存储到 /usr/local/ 目录下。
3.-XX:OnError 选项, 发生致命错误时(fatal error)执行的脚本。
例如, 写一个脚本来记录出错时间, 执行一些命令, 或者 curl 一下某个在线报警的 url. 示例用法:java -XX:OnError=“gdb - %p” MyApp
可以发现有一个 %p 的格式化字符串,表示进程 PID。
4.-XX:OnOutOfMemoryError 选项, 抛出 OutOfMemoryError 错误时执行的脚本。 -XX:ErrorFile=filename 选项, 致命错误的日志文件名,绝对路径或者相对路径。 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1506,远程调试