类的加载,连接与初始化
加载
指的是将类的.class文件中的二进制数据读入到内存当中,将其放在运行时数据区的方法区内,然后在内存中创建一个java.lang.Class对象.
类的加载方式
- 从本地系统中直接加载
- 从网络下载.class
- 从zip, jar等归档中加载.class文件
- 从专有数据库中提取.class文件
- 将Java源文件动态的编译为.class文件
连接
- 验证确保被加载的类的正确性
- 准备为类的静态变量分配内存,并将其初始化为默认值
- 解析把类中的符号引用转换为直接引用
类的使用
- 主动使用
- 创建类的实例
- 访问某个类或接口的静态变量或对静态变量赋值
- 访问类的静态方法
- 反射
- 初始化一个类的子类
- Java虚拟机启动时被标明为启动类的类
- JDK1.7开始提供的动态语言支持:java.lang.invoke.MethodHandler实例的解析结果REF_getStatic, REF_PUTsTATIC, REF_invokeStatic句柄对应的类没有初始化,则初始化。
- 被动使用
初始化
所有的Java虚拟机实现必须在每个类或接口被Java程序"首次主动使用"时才进行初始化.
注意
- 当一个常量的值并非编译期间可以确定的,那么其值就不会被放到调用类的常量池中,这时在程序运行时,会导致主动使用这个常量所在的类,显然会导致这个类被初始化.
主动使用的方式
- 创建类的实例
- 访问某个类或接口的静态变量,或者对该静态变量赋值
- 调用类的静态方法
- 反射如Class.forName(“com.path.ClassName”)
- 初始化一个类的子类
- Java虚拟机启动时被标明为启动类的类
- JDK1.7开始提供的动态语言支持.
虚拟机参数
Tips
说明
- -XX:+<option>, 表示开启option选项
- -XX:-<option>, 表示关闭option选项
- -XX:<option>=<value>, 表示将option选项的值设置为value
参数
- –XX:+TraceClassLoading: 用于追踪类的加载信息并打印出来.
- -Xss1M: 设置最大调用深度,防止栈举出。StackOverflowError。
- -XX:MaxTenuringThreshold: 指定新征伐对象经过多少次回收后进入老年代,默认为15次。
- -XX:PretenureSizeThreshold: 指定对象的大小超过在指定的大小之后,直接晋升老年代。
- TLAB
- -XX:+UseTLBA: 使用TLAB, 默认开启。
- -XX:+TLABSize: 设置TLAB大小
- -XX:TLABRefillWasteFraction:设置维护进入TLAB空间的单个对象大小,他是一个比例值,默认为64,即如果对象大于整个空间的1/64,则在堆创建对象。
- -XX:+printTLAB:查看TLAB。
- -XX:ResizeTLAB: 自调整TLABRefillWasteFraction阀值。
- 垃圾收集器
- -XX:UseSerialGC: 使用串行垃圾回收器。
- -XX:+useParNewGC: 新生代使用ParNew回收器。老年代使用串行回收器。
- -XX:parallelGCThreads: 指定Parnew收集器的线程数。
- ParallelGC
- -XX:MaxGCPauseMills: 设置最大垃圾收集停顿时间。
- -XX:GCTimeRatio: 设置吞量大小,它是一个0到100之间的整数,默认为100。
- -XX:UseAdaptiveSizePolicy: 打开自适应模式。
- CMS
- -XX:+UseConcMarkSweepGC
- -XX:ConcGCThreads:设置并发线程数量。
虚拟机调优策略
JVM参数调优主要设置堆内存,主要让GC不要去频繁回收垃圾,减少对老年代的回收。配置时让-Xms与-Xmx一致。
- 初始内存和最大内存越大,吞量就大,初始内存与最大内一致。
- 并行回收比串行回收吞量要大,多核多线程吞吐。
- 垃圾回收机制次数越少,说明性能越高。
垃圾回收算法
Q
- 为什么新生代和老年代使用不一样的压缩算法。
主要回收算法
引用计数算法
问题
- 无法处理交叉引用的情况
复制算法
一般在新生代使用
标记清除算法
一般老年代才会使用问题:
- 内存碎片问题。
标记压缩算法
一般在老年代使用, 是标记清除算法的升级。
分代算法
根据对象的特点把内存分成N块,而后根据每个内存的特点使用不同的算法。对于新生代和老年代来说,新生代回收频率很高,但是每次回收消耗时间很短,而对老年代回收频率较低,但每次消耗时间较长,所以应当尽量减少老年代的回收。
分区算法
JDK1.7新增的算法。主要是将整个内存分为N个独立空间,每个小空间都可以独立使用,这样细粒度的控制一次回收多少个小空间,而不是对整个内存空间进行GC,从而提升性能,并减少GC的停顿时间。
内存分区
- 程序计数器指向当前线程正在执行的字节码指令的地址
- 栈/虚拟机栈存储当前线程运行方法所需要的数据、指令、返回地址。以栈帧(局部变量表,操作数栈,动态链接,出口。。。)为单位。
- 本地方法栈
- 方法区类信息,常量,静态变量,JIT JDK1.8以前还有永久代。
- 堆堆分为新生代和老年代。新生代分为eden区,s0区,s1区(hotSpot比例为8:1:1)。新生区与老年区的大小一般比例是1比2或者1比3 TLAB Thread Local Allocation Buffer 即线程本地分配缓存,线程专用的个内存分配区域,是为了加速对象分配而生的。每一个线程都会产生一个TLAB,该线程独享的工作区域。
对象创建
对象创建流程
- 尝试栈上分配。
- 尝试TLAB区
- 是否满足进入老年代
- eden分配。
垃圾收集器
分类
- 串行回收器
- 并行回收器
- CMS Concurrent Mark Sweep 并发标记清除回收器。 -XX:CMSInitiatingOccupancyFraction默认为68,即当老年代的空间使用率达到68%的时候,会执行CMS回收。
- G1
助记符
- ldc 表示将int, float或是String类型的常量值从常量池中推送到栈顶.
- bipush 表示将单字节(-128~127)的常量推送至栈顶.
- sipush 表示将一个短整型常量值(-32768~32767)推送至栈顶.
- iconst_1 表示将一个int类型1推送至栈顶. (iconst_m1 ~ iconst_5)
- anewarray 表示创建一个引用类型的(如类,接口,数组)数组,并将其引用值压入栈顶.
- newarray 表示创建一个指定的原始类型(如float, int, char等)数组,并将其引用值压入栈顶
JVM监控工具
jps
查询java进程的一些基本信息,比如PID
jvisualvm
jconsole
jcmd
jmc
一个界面的大成功能
jhat
分析堆转储文件
jmap
使用jmap命令生成dump文件
问题
内存溢出与内存泄露
内存泄露:对象已经没有被应用程序使用,但是垃圾回收器没办法移除它们,因为还在被引用着。内存溢出:需要的内存比支持的内存要多。
垃圾回收线程频繁使用的缺点
垃圾回收执行时所有的其他线程都会暂停,会影响程序效率。