万字长篇,图文并茂!一篇与面试官和蔼交流的深入了解JVM(JDK8)果断收藏了!

文章目录

面试系列

1、类加载机制

类加载过程分为 加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载

1、加载 在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象
等等,在加载阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

2、验证 校验字节码文件的正确性
3、准备 给类的静态变量分配内存,并赋予默认值

4、解析 将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据
所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程
序运行期间完成的将符号引用替换为直接引用5、初始化 对类的静态变量初始化为指定的值,执行静态代码块

2、双亲委派机制(先找父亲加载,不行再由儿子自己加载)
2.1、类加载器

1、根类加载器(Bootstrap
classLoader
):负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等
2、扩展类加载器(ExtClassLoader):负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包
3、应用加载器(AppClassLoader):负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类,负责加载用户自定义路径下的类包

2.2、加载器初始化过程

类运行加载全过程会创建JVM启动器实例sun.misc.Launcher。sun.misc.Launcher初始化使用了单例模式设计,保证一个JVM虚拟机内只有一个sun.misc.Launcher实例。在Launcher构造方法内部,其创建了两个类加载器,分别是sun.misc.Launcher.ExtClassLoader(扩展类加载器)和sun.misc.Launcher.AppClassLoader(应用类加载器)。
JVM默认使用launcher的getClassLoader()方法返回的类加载器AppClassLoader的实例来加载我们的应用程序。

2.3、双亲委派机制

应用程序类加载器AppClassLoader加载类的双亲委派机制源码,AppClassLoader的loadClass方法最终会调用其父类ClassLoader的loadClass方法,该方法的大体逻辑如下:
首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。
如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name,
false);).或者是调用bootstrap类加载器来加载。
如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。

2.4、为什么要设计双亲委派机制?

1、沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改
2、避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性

2.5、全盘负责委托机制

“全盘负责”是指当一个ClassLoder装载一个类时,除非显示的使用另外一个ClassLoder,该类所依赖及引用的类也由这个ClassLoder载入

2.6、自定义类加载器示例

自定义类加载器只需要继承 java.lang.ClassLoader 类,该类有两个核心方法,一个是loadClass(String,
boolean),实现了双亲委派机制,还有一个方法是findClass,默认实现是空方法,所以我们自定义类加载器主要是重写findClass方法。

3、tomcat怎么破解类加载机制

1、commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;

2、catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;

3、sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;

4、WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见,比如加载war包里相关的类,
每个war包应用都有自己的WebappClassLoader,实现相互隔离,比如不同war包应用引入了不同的spring版本,这样实现就能加载各自的spring版本;

5、模拟实现Tomcat的JasperLoader热加载

原理:后台启动线程监听jsp文件变化,如果变化了找到该jsp对应的servlet类的加载器引用
(gcroot),重新生成新的JasperLoader加载器赋值给引用,然后加载新的jsp对应的servlet类,之前的那个加载器因为没有gcroot引用了,下一次gc的时候会被销毁

=>总结:每个webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器,打破了双亲委派机制。

4、内存模型
4.1、线程私有区域

程序计数器:是当前线程所执行的字节码的行号指示器,无OOM
虚拟机栈:是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack
Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、
方法返回值和异常分派( Dispatch
Exception)。栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。
本地方法栈:和 Java Stack 作用类似, 区别是虚拟机栈为执行 Java 方法服务, 而本地方法栈则为Native 方法服务,
如果一个 VM 实现使用 C-linkage 模型来支持 Native 调用, 那么该栈将会是一个 C 栈,但 HotSpot VM
直接就把本地方法栈和虚拟机栈合二为一。

4.2、线程共享区域

堆-运行时数据区:是被线程共享的一块内存区域,创建的对象和数组都保存在 Java
堆内存中,也是垃圾收集器进行垃圾收集的最重要的内存区域。由于现代 VM 采用分代收集算法, 因此 Java 堆从 GC 的角度还可以细分为:
新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年代

方法区/永久代(1.8之后元空间):用于存储被 JVM 加载的类信息**、常量、静态变量、**即时编译器编译后的代码等数据. HotSpot
VM把GC分代收集扩展至方法区, 即使用Java堆的永久代来实现方法区, 这样 HotSpot 的垃圾收集器就可以像管理 Java
堆一样管理这部分内存, 而不必为方法区开发专门的内存管理器(永久带的内存回收的主要目标是针对常量池的回收和类型的卸载, 因此收益一般很小)。

运行时常量池(Runtime Constant Pool)是方法区的一部分。Class
文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool
Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

直接内存

jdk1.4后加入NIO(New
Input/Output)类,引入了一种基于通道与缓冲区的I/O方式,它可以使用native函数库直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。可以避免在Java堆和Native堆中来回复制数据
直接内存的分配不会受到Java堆大小的限制.避免大于物理内存的情况

5、对象的创建

1、类加载检查

虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。new指令对应到语言层面上讲是,new关键词、对象克隆、对象序列化等
2、分配内存

在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类 加载完成后便可完全确定,为对象分配空间的任务等同于把
一块确定大小的内存从Java堆中划分出来。 //如何划分内存? 1、“指针碰撞”(Bump the Pointer)(默认用指针碰撞)
如果Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离。
2、“空闲列表”(Free List)
如果Java堆中的内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。
//解决并发问题的方法 1、CAS(compare and swap)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值