1. JVM内存模型
堆:线程共享,new出来的对象,类的实例区,内部又分为新生代和老年代,新生代又分为EDEN、s1、s2;
虚拟机栈:或者叫线程栈,每个线程分配一个虚拟机栈,属于线程私有,方法执行时创建栈帧,栈帧里面主要存放:局部变量、操作数栈(计算过程中的中间结果)、动态链接(运行时常量池中该栈帧所属方法的引用)和方法出口(方法最初被调用的位置)
元空间:或者叫方法区,线程共享,主要是运行时常量池:类信息、常量、静态变量、即时编译器编译后的代码缓存
程序计数器:线程私有,记录当前线程执行的代码位置(字节码)
本地方法栈:线程私有,虚拟机运行时使用到的内地Native方法(C++的方法)
2. 垃圾回收算法
- 复制算法
- 内存换分为等大的两块
- 每次使用其中的一块,这一块使用满后,将存活的对象复制到另一块上,把已使用的内存清理掉
优缺点:内存效率高,不易产生碎片,但是可用内存压缩为原来的一半,如果存活的对象增多,算法效率会降低
- 标记-清除算法
- 第一阶段,标记需要回收的对象(根据根可达算法)
- 回收被标记对象占用的空间
优缺点:内存碎片化严重,大对象可能找不到连续的内存空间
- 标记-整理算法
- 标记过程和标记清除算法一致,先标记出需要回收的对象
- 标记后将存活对象移向内存一端
- 清除端边界外的对象
3. JVM使用何种垃圾回收算法
- 新生代使用复制算法:每次现将eden和s1中的对象复制到s2中,然后回收eden和s1,之后再将s2复制到s1上
- 老年代使用标记整理算法:老年代每次回收少量对象
- 对象头的markword中存放的有对象年龄,每次新生代回收,回收不掉的话,年龄+1
3. 如何确定垃圾对象或者说哪些可以作为GCRoot
JVM在进行垃圾回收时,需要确定垃圾对象,也就是没有被引用的对象。但是直接找垃圾对象比较耗时,所以先找非垃圾对象,也就是正常对象。那么就需要从某些根出发,寻找正常对象,这些根就被称作GCRoot,主要包括:
JVM栈中的引用的对象、方法区中的静态变量和常量(字符串常量池中的引用)、本地方法栈中引用的对象、正在运行的线程
4. 双亲委派
什么是双亲委派:类加载器收到类的加载请求后,不会先去加载,而是去委派给父类加载器去完成,主要包含三层classloader:
- APPClassLoader:主要负责加载应用程序主函数类下的类,也就是CLASSPATH(JAVA_HOME/lib)下的类
- ExtClassLoader:主要负责jre/lib/ext目录下的一些扩展类
- BootstrapLoader:核心类库的加载,比如java.lang包下的
过程:
- 类加载器收到类的加载请求后,不会先去加载,而是先去委派给父类去加载,也就是按照app==》ext==》bootstrap的顺序
- 每一层类加载器都会按照这个方式来加载,所有的加载请求最终都会到顶层的类加载其中去尝试加载
- 父加载器反馈无法完成加载时,子加载器才会尝试自己去加载
原因或者好处
- 避免核心类被篡改,比如String类
- 防止类的重复加载,加载过就不需要再次加载
破坏
JNDI:常见就是jdbc驱动,数据库Driver定义在jdk中,具体实现是各个数据库服务商。DriverManager要加载Driver接口实现类,然后进行管理,但是DriverManager是由启动类加载器进行加载的,而这个启动类加载器默认值加载JAVA_HOME下面的lib,但我们真正要加载的是各个实现类,需要有系统类加载器进行加载,这个时候就需要启动类加载器委托系统类加载器去加载Driver实现类,从而破坏了双亲委派。
Tomcat热部署也打破了:一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离。
5. 对象的创建过程
class T{
int m = 8;
}
T t = new T();
- 先申请空间,成员变量根据类型设置默认值(数值类型都是0,布尔类型默认是false,字符类型是null)
- 调用对象的构造方法,把成员变量改成设置的值
- 建立关联
6. 对象在内存中的存储布局
对象在内存中的布局可以通过Java Object Layout插件打印出来。
普通对象分为四部分:
- markword:4个字节
- class pointer:类型指针,开启压缩时占用4个字节,关闭指针压缩时占8个字节,对象属于哪个类,就指向哪个类的指针
- instance data:实例数据,也就是成员变量
- padding:对齐,保证对象大小能够被8整除。为什么要被8整除?8字节大小,为64bit,也就是64位对齐,虚拟机为64位,一次性存储64位最方便。
7. 对象头包含哪些内容
markword和class point
- markword主要包含以下内容:
- 锁状态:无锁、偏向锁、自旋锁、重量级锁
- hashcode
- GC信息,标记对象属于哪种颜色【三色标记算法】还有分代年龄
8. 对象如何定位【不重要】
- 直接指针:
大多数JVM都是直接指针,直接指针t是直接指向堆里面的实例数据,实例数据通过类型数据指针指向方法区的对象。
优点: 直接访问,速度快
缺点: GC需要移动对象的时候稍麻烦 - 间接指针:
t指向指针数组,指针数组里面包含实例数据指针和类型数据指针,然后实例数据指针和类型数据指针分别指向堆中的数据和方法区中的class
优点: 对象小,垃圾回收时不用频繁改动t
缺点: 两次访问,速度稍慢
9. 对象怎么分配
- 对象new出来的时候先尝试在栈【虚拟机栈】上分配,栈上能分配成功直接在栈上分配。如何确定能否在栈上分配?逃逸分析【栈帧内部对象只有自己使用,对象如果有其他栈帧引用,就分配在堆上】和标量替换。栈上分配的好处?使用完直接弹出就可以,操作系统直接管理。绝大多数对象都不能在栈上直接分配。
- 栈上不能分配,看对象大小,对象过大的话直接分配到老年代上【通过JVM参数控制】
- 以上不满足,对象分配到堆上年轻代的伊甸园区【EDEN】,通过TLAB机制来确定存放地址

10. 为什么不使用C++对象直接表示Java对象
因为有虚函数表【实现JAVA多态】存在,C++占用空间会大
11. Class实例究竟在Method Area还是在Heap?
堆里面。。。底层的话,C++对象指针存放在方法区中,这个指针指向堆里面的对象,所以本质上还是存放在堆里面
2866

被折叠的 条评论
为什么被折叠?



