深圳建站网站模板,最常用免费h5制作平台,深圳英文网站建设公司,上海网站建设推荐秒搜科技1、JVM基础概念
#xff08;1#xff09;JVM、JRE、JDK
JRE#xff1a;JVM基本类库组成的运行环境就是JRE。JVM自己是无法完成一次编译#xff0c;处处运行的#xff0c;需要有一个基本类库告诉JVM如何操作运行#xff0c;如如何操作文件#xff0c;连接网络等#x…1、JVM基础概念
1JVM、JRE、JDK
JREJVM基本类库组成的运行环境就是JRE。JVM自己是无法完成一次编译处处运行的需要有一个基本类库告诉JVM如何操作运行如如何操作文件连接网络等JVM运行时会一次性加载基本类库JDKJDK中除了包含JRE同时还包含一些小工具如javacjar等如果只要运行java程序只需JRE即可三者之间关系JDKJREJVM 2java程序运行
编写好一个java程序后使用javac进行编译后会生成字节码文件java程序中使用的输出等模块是JRE里提供的基本类JVM采用栈架构其指令由操作码opcode操作数组成操作码只有1个字节长度0-255指令数不能超过256条我们运行.class文件时会启动一个JVM进程JVM通过opcode操作数来执行程序执行方式有解释执行将opcode操作数翻译为机器码文件JIT及时编译在特定条件下会将字节码编译为机器码文件之后才执行 2、JVM内存管理
1JVM内存布局
堆中的数据是共享的占用内存最大执行字节码文件的模块为执行引擎线程切换通过程序计数器进行 2虚拟机栈
虚拟机栈是基于线程的main方法启动后以线程方式运行线程的生命周期和栈的生命周期一样栈中每一条数据都是栈帧每调用一个java方法就会创建一个栈帧并入栈执行完改该方法后便会出栈所有栈帧出栈后线程就会结束 3程序计数器
程序计数器可以看作当前线程执行字节码的行号指示器存储的是当前线程的进度程序计数器有线程创建时产生配合虚拟机站完成计算操作 4堆
申请的对象都是存储在堆中垃圾回收的对象是堆堆在程序启动时创建随着对象不断创建堆空间会越来越小就需要对堆内不常用的对象进行回收即GCjava对象分为基本数据类型和普通对象基本数据类型存储在栈中对于普通对象JVM会在堆中创建对象其他地方使用该应用堆中的数据是线程共享的栈中的数据是线程私有的 5元空间
Java8之前类创建对象的模板都是存储在永久代中且不会被JVM回收随着类的增多会造成JVM内存溢出java8中元空间替代了永久代在非堆上存储JVM不会再出现方法区的内存溢出但无限使用元空间会造成操作系统挂掉可以通过-XX:MaxMetaspaceSize控制元空间大小 3、类加载
1类加载过程 加载将.class文件加载到JVM的方法区中验证判断.class是否可以加载进入JVM中不合法的直接抛出异常低版本的JVM无法加载高版本类库准备为类变量分配内存并初始化值此时实例对象还未分配内存所以此时是在方法区中进行的如下图类变量有两次赋值一次在准备阶段赋予默认值一次在初始化阶段赋予程序员指定的值而局部变量不会初始化赋值没有默认值不指定会报错 解析将符号应用替换为直接应用。符号应用就是我们定义的字面量如int a1a就是符号应用直接应用是直接执行目标对象的指针或偏移量初始化初始化成员变量类信息只会存储一份到方法区中类加载只会加载一次所以static变量和staic代码块只会加载一次JVM保证子类初始化之前父类已经初始化
如下代码第一次new时执行顺序 父类A的静态方法块---子类B的静态方法块--父类A的构造方法--子类B的构造方法第二new时执行顺序 父类A的构造方法--子类B的构造方法 public class A {static {System.out.println(A static 代码块);//只会执行一次}public A(){System.out.println(A 构造方法);}
}class B extends A{static {System.out.println(B static 代码块);//只会执行一次}public B(){System.out.println(B 构造方法);}public static void main(String[] args) {A a new B();//第一次会调用静态代码块B b new B();//不会调用静态代码块}
} 2类加载器
双亲委派机制除了顶层的启动类加载器外其余加载器都会在加载类时先委托给父类加载父类无法加载才会交给子类去加载双亲委派机制可以避免我们自定义的类去覆盖java类库保证java程序的安全如下我们使用写一个java.lang.String运行main方法时会报错因为加载String类时是由父类加载器加载加载的是java类库中的String而java类库中的String类没有main方法所以会报错 Tomcat中会破坏双亲委派机制每一个web应用都有一个WebappClassLoader加载器会加载应用程序类只有当自己加载不到才会委托给父类使用该类加载器JVM中出现同一个第三方库不同版本应用程序去使用他不会发生冲突他们由各自的类加载器加载相互隔离类对象是否相同由类加载器的命名空间和类对象本身决定不同类加载器命名空间不同 4、JVM异常处理
1异常表
每一个方法都会携带一个异常表表中每一行代表一个异常处理器并且由from指针、to指针、target指针和异常处理类型组成这些指针指向的是字节码的索引from指针到to指针表示异常监控的范围try中代码target指针表示异常起始处理器的开始位置即catch代码如下图异常监控范围为0-7不包括7异常处理器开始范围为10 2异常处理
程序出现异常时JVM会开始从上到下遍历异常表中每一行当出现异常的字节码索引在异常表中的某一行的监控范围内则会判断监控表的异常类型是否和当前程序抛出的异常类型一致如果一致会将字节码索引转到target指针如果遍历完异常表的每一行还是没有找到可以处理异常的异常处理器则会将该方法的栈帧出栈继续遍历调用该方法的调用者的异常表重复异常表查找操作最坏会遍历当前栈上所有方法的异常表 3finally块
finally代码块的内容会赋值到try和catch块中异常表中会生成一行异常处理条目监控整个try-catch并且捕获异常在执行完finally块后若有捕获异常则会抛出异常
5、OOM
1GC Root
发生GC回收时对于每一个对象JVM总是回去寻找引用他的祖先如果这个引用祖先已经挂了就会将该对象回收能够躲避垃圾回收的祖先就是GC root从GC root向下搜索会产生一条Refrence Chain链当任何一个对象不能和任何一个GC root产生关系时就会被回收GC过程是找出活的对象并认定其他对象为”无用” GC Root有
java线程中当前正在被调用的方法的引用类型参数、局部变量、临时值等即和栈帧相关的各种引用所有被加载的java类java类的引用类型的静态变量运行时常量池里的引用类型String或Class类型用于同步的监控对象如调用了对象的wait方法
大致分为三类
线程中的相关引用类静态变量的引用JNI引用
2引用类型
强引用当内存不足的时候JVM会抛出OutOfMemoryError错误即使程序终止该对象也不会被回收只有将GC Root与其断开该对象才会被回收如我们平常new一个对象即为一个强引用大量对象被创建且不能被回收时会造成内存泄漏软引用维护一些可有可无的对象只有当堆内存空间不够时才会回收该对象如果回收了软引用后JVM内存空间任然不足则会抛出OutOfMemoryError软引用可以和引用队列ReferenceQueue联合使用当软引用所引用的对象被回收后就会加入到该队列中弱引用GC回收时不管空间是否足够都会回收弱引用所指向的对象与WeakRefenece联合使用虚引用采用该引用的对象在GC回收时都会被回收虚引用必须和引用队列联合使用。当GC回收对象时若该对象有虚引用则会将虚引用加入到引用队列中在对象被回收之前会执行一些逻辑
3OOM原因
OOM可能发生的区域有堆、本地方法栈、虚拟机栈、元空间内存容量过小需要调整堆大小错误的引用方式发生内存泄漏。没有及时清理与GC Root相关联系如线程池中的线程复用时没有清理ThreadLocal会造成内存泄漏堆接口参数没有校验传入参数超出范围堆外内存无限制使用会导致操作系统资源耗尽
6、垃圾回收
1垃圾回收算法
标记清除算法根据GC Root遍历所有可达对象进行标记清除掉未标记的对象该算法会造成内存碎片如下当GC回收后剩余总空间还有7K分配一个6K空间无法分配成功 标记复制算法会预留一半的空间每次GC回收时会将活对象复制到空闲的空间中去所有算法中效率最高但会造成空间浪费 标记整理GC回收时会将存活对象整理到一起不会造成空间浪费同时也不会出现内存碎片但效率最低 2年轻代
存活时间比较短的对象都存储在年轻代中使用的清除算法是标记复制算法因为年轻代中存活的对象比较少使用标记复制算法可以大大提高效率年轻代分为伊甸园去Eden和两个幸存区Survivor对象会先分配到伊甸园区当Eden区分配满时会触发年轻代GCMinor GC过程如下 Eden第一次GC先将Eden区存活的对象移动到一个幸存区Eden再次GC会采用标记复制算法会将清理from区和Eden区存活的对象被移动到To区最后将From区清空即可
EdenFromTo811会浪费10%的空间-XXSurvivorRatio可以设置默认为8JVM会为每一个线程分配一个缓冲区TLABTLAB在Eden区中每个线程可以在TLAB上分配对象避免锁竞争但如果分配对象大小超过TLAB大小则会在Eden区的公共区域分配 3老年代
老年代一般采用标记清除、标记整理算法Major GC因为对象存活比较久空间占用率比较大拷贝不划算对象进入老年代途径 提升根据对象的年龄判断是否需要进入老年代每发生一次Minor GC存活下来的对象年龄会加1直到年龄超过阈值对象就会进入老年代若对象不可达会等到老年代发生GC时才会被回收阈值可以通过参数 ‐XX:MaxTenuringThreshold 进行配置最大值是 15分配担保年轻代中每次存活的对象都会进入Survivor区中这个区域只有10%的大小但我们无法保证每次存活对象的大小不超过10%当Survivor空间不够时就需要依赖老年代进行担保直接在老年代中分配对象大对象直接分配到老年代当对象大小超过指定大小会直接在老年代中分配对象通过设置该参数 -XX:PretenureSizeThreshold默认为0即对象都在年轻代分配动态对象年龄判断当幸存区中相同年龄对象的大小的和超过幸存区的一半幸存区中大于等于这个年龄的对象都会进入老年代
4年轻代垃圾回收器
Serial 垃圾收集器处理GC只有一个线程GC时会停止一切用户进程通常用于客户端应用因为客户端应用通常不会建立过多对象ParNew垃圾收集器处理GC时有多个线程GC时同样会停止用户线程存在线程切换开销在单CPU下性能比Serial差追求降低用户停顿时间Paraller Scavenge垃圾收集器也是多线程处理追求CPU高吞吐量能够在较短时间内完成指定任务
5老年代垃圾收集器
Serial Old垃圾回收器同样为单线程版本采用标记整理算法而年轻代中采用标记复制算法Parallel Old垃圾回收器多线程处理追求CPU高吞吐CMS垃圾回收器以获取最短用户停顿时间为目标的垃圾收集器采用用户线程和回收线程并发执行用户不会感觉到明显的停顿
7、CMS垃圾收集器
CMS全称并发标记清除垃圾回收器在年轻代使用标记复制算法在老年代使用标记清除算法会造成内存碎片只有通过full GC时进行整理主要是为了降低清除老年代时避免长时停顿UseCMSCompactAtFullCollection默认开启表示Full GC时需要整理内存不能和用户进程并发会造成停顿
回收过程如下
初始标记只标记和GC Root直接相连的对象因为GC Root在追踪活对象时最为耗时同时还要标记与年轻代相关的对象会发生STW并发标记在初始标记阶段进行并发标记标记所有可达对象。该阶段最为耗时但可以和用户进程并行。在该阶段可能会有对象引用发生变化引用发生变化的对象会被标记为dirty用于后续重新扫描并发预清理并发预清理被标记为dirty状态的对象将dirty状态对象重新标记清除dirty状态该阶段和用户进程并发因此可能还会出现dirty状态并发可取消的预清理重新标记阶段是需要STW停止用户线程因此在满足某些条件时可以终止标记避免会扫年轻代大量对象最终标记第二次STW标记所有存活对象。之前的并发预清理阶段可能发生多次可能赶不上应用变化清况所以最终标记会STW停止应用进程最终标记所有存活对象并发清除与用户进程并发进行清除所有不可达对象可能会产生新的垃圾浮动垃圾该部分垃圾只能等到下一次GC时才能被回收并发重置重置与CMS相关的内部结构为下一次GC做准备