1.jvm基本结构
编译器会将我们编写的Java文件编译成 .class 文件,JVM会加载并执行 .class 文件。下面的图展示了JVM的整体架构
1,类加载子系统负责动态加载类,在运行时(而非编译时),当一个类初次被引用的时候,它将被加载、链接、初始化。
2,运行时数据区,运行时数据区可以划分为五个区域:
方法区:全局共享资源,所有的类级别的数据都会存储到这里,包括静态变量。
堆区:线程共享的资源,所有的对象和其对应的实例变量和数组都被存储在这个区域。
堆栈区:不共享资源,对于每个线程,将创建单独的运行时堆栈。对于每个方法调用,将在堆栈存储器中产生一个条目,称为堆栈帧。
PC寄存器:每个线程都有单独的PC寄存器,用于保存当前执行指令的地址。一旦执行指令,PC寄存器将被下一条指令更新
本地方法堆栈:本地方法堆栈保存本地方法信息。对于每个线程,将创建一个单独的本地方法堆栈。
3,执行引擎读取字节码并逐个执行
2.类加载
类的生命周期:
加载<将.class文件加载到内存>
连接(验证<验证字节码文件正确性>
准备<给类的静态变量分配内存并赋予默认值>
解析<类装载器装入类所引用的其他所有类>
初始化<给类的静态变量赋予正确的初始值执行静态代码块>--
使用
卸载
类加载器的种类:
启动类加载器(bootstrap classloader):加载re目录的类
扩展类加载器(extension classloader):加载ext目录的类
系统类加载器(application classloader)
用户自定义的类加载器
加载机制:
双亲委托机制:当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。简单的来说就是:当一个class文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理会先检查自己是否已经加载过,如果没有再往上。注意这个过程,知道到达BootstrapclassLoader之前,都是没有哪个加载器自己选择加载的。如果父加载器无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。可以避免重复加载,父类已经加载了,子类就不需要再次加载更加安全。很好的解决了各个类加载器的基础类的统一问题,如果不使用该种方式,那么用户可以随意定义类加载器来加载核心api,会带来相关隐患。
全盘委托机制:显示的指定类加载器,当一个类运行时,可能有其他的类,这时由应用类加载器委托给扩展类加载器是否加载这些类,扩展类加载器再次向上委托引导类加载器是否加载这些类,引导类加载器判断后将有的类进行加载向内存中返回class对象后,再由扩展类加载器中有的类进行加载返回class对象,剩下全部有应用类加载器进行加载.
3.jvm调优工具
jps 查看java进程
jinfo 查看正在运行的java程序的扩展参数
jstat 查看堆内存的使用量以及加载类的数量
jmap 查看统计(类的使用情况,堆内存dump)
jstack 用于生成jvm当前时刻的线程快照
4.垃圾回收算法:
复制算法:<解决效率问题> ----------适合新生代
标记清除算法:<效率比较低,会产生大量不连续的碎片>
标记整理算法:<根据年老代特点提出的一种标记算法,先移动,再清理>
分代收集算法
不使用计数方式判断对象是否引用的原因:循环引用的问题
现在广泛使用的是:可达性分析算法
四种引用方式:强软弱虚
finalize===自救只执行一次
5.垃圾收集器
Serial<串行,单线程>
ParNew<Serial多线程版>
Parallel Scacenge<关注点是吞吐量>
G1<面向服务器的垃圾收集器>
CMS<并发收集短停顿(对cpu敏感、无法处理浮动垃圾,标记清楚会产生碎片)>
来源:CSDN
作者:上士闻道,勤而行之;中士闻道,若存若亡;
链接:https://blog.csdn.net/dushihao626/article/details/103999547