1、JVM的主要组成部分及其作用?
- Class loader(类装载): 根据给定的全限定名类名(如:java.lang.Object)来装载class文件到Runtime data area中的method area。
- Execution engine(执行引擎): 执行classes中的指令。
- Native Interface(本地接口): 与native libraries交互,是其它编程语言交互的接口。
- Runtime data area(运行时数据区域): 这就是我们常说的JVM的内存。
运行流程:
首先通过编译器把 Java 代码转换成字节码,类加载器(ClassLoader)再把字节码加载到内存中,将其放在运行时数据区(Runtime data area)的方法区内,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
2、JVM运行时数据区
程序计数器(Program Counter Register) | 当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成; |
---|---|
Java 虚拟机栈(Java Virtual Machine Stacks) | 用于存储局部变量表、操作数栈、动态链接、方法出口等信息; |
本地方法栈(Native Method Stack) | 与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的; |
Java 堆(Java Heap) | Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存; |
方法区(Methed Area) | 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。 |
3、深拷贝 和 浅拷贝
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
4、堆 和 栈 区别
存放的内容
堆存放:对象的实例和数组。因此该区更关注的是数据的存储
栈存放:局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行。
静态变量放在方法区
静态的对象还是放在堆。
程序的可见度
堆对于整个应用程序都是共享、可见的。
栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同。
5、队列 和 栈 区别
队列和栈都是被用来预存储数据的。
- 操作的名称不同:队列的插入称为入队,队列的删除称为出队。栈的插入称为进栈,栈的删除称为出栈。
- 可操作的方式不同:队列是在队尾入队,队头出队,即两边都可操作。而栈的进栈和出栈都是在栈顶进行的,无法对栈底直接进行操作。
- 操作的方法不同:队列是先进先出(FIFO)。而栈为后进先出(LIFO)
6、内存泄露
内存泄漏是指不再被使用的对象或者变量一直被占据在内存中。理论上来说,Java是有GC垃圾回收机制的,也就是说,不再被使用的对象,会被GC自动回收掉,自动从内存中清除。
但是,即使这样,Java也还是存在着内存泄漏的情况,java导致内存泄露的原因很明确:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景。
7、类装载的执行过程?
- 加载:根据查找路径找到相应的 class 文件然后导入;
- 验证:检查加载的 class 文件的正确性;
- 准备:给类中的静态变量分配内存空间;
- 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
- 初始化:对静态变量和静态代码块执行初始化工作。