Skip to content

1、类加载的过程是什么?

加载

该阶段虚拟机需要完成三件事:① 通过一个类的全限定类名获取定义类的二进制字节流。② 将字节流所代表的静态存储结构转化为方法区的运行时数据区。③ 在内存中生成对应该类的 Class 实例,作为方法区这个类的数据访问入口。

验证

确保 Class 文件的字节流符合约束。如果虚拟机不检查输入的字节流,可能因为载入有错误或恶意企图的字节流而导致系统受攻击。验证主要包含四个阶段:文件格式验证、元数据验证、字节码验证、符号引用验证。

验证重要但非必需,因为只有通过与否的区别,通过后对程序运行期没有任何影响。如果代码已被反复使用和验证过,在生产环境就可以考虑关闭大部分验证缩短类加载时间。

准备

为类静态变量分配内存并设置零值,该阶段进行的内存分配仅包括类变量,不包括实例变量。如果变量被 final 修饰,编译时 Javac 会为变量生成 ConstantValue 属性,准备阶段虚拟机会将变量值设为代码值。

解析

将常量池内的符号引用替换为直接引用。

符号引用以一组符号描述引用目标,可以是任何形式的字面量,只要使用时能无歧义地定位目标即可。与虚拟机内存布局无关,引用目标不一定已经加载到虚拟机内存。

直接引用是可以直接指向目标的指针、相对偏移量或能间接定位到目标的句柄。和虚拟机的内存布局相关,引用目标必须已在虚拟机的内存中存在。

初始化

直到该阶段 JVM 才开始执行类中编写的代码。准备阶段时变量赋过零值,初始化阶段会根据程序员的编码去初始化类变量和其他资源。初始化阶段就是执行类构造方法中的 `` 方法,该方法是 Javac 自动生成的。

2、谈谈 JVM 中的常量池

JDK 1.8 开始

1、 字符串常量池:存放在堆中,包括 String 对象执行 intern() 方法后存的地方、双引号直接引用的字符串

2、 运行时常量池:存放在方法区,属于元空间,是类加载后的一些存储区域,大多数是类中 constant_pool 的内容

3、 类文件常量池:constant_pool,JVM 定义的概念

3、JVM 内存区域

JVM 内存区域主要分为线程私有区域【程序计数器、虚拟机栈、本地方法区】、线程共享区域【JAVA 堆、方法区】、直接内存。

线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束 而 创建/销毁(在 Hotspot VM 内, 每个线程都与操作系统的本地线程直接映射, 因此这部分内存区域的存/否跟随本地线程的生/死对应)。

线程共享区域随虚拟机的启动/关闭而创建/销毁。

直接内存并不是 JVM 运行时数据区的一部分, 但也会被频繁的使用: 在 JDK 1.4 引入的 NIO 提供了基于Channel与 Buffer的IO方式, 它可以使用Native函数库直接分配堆外内存, 然后使用DirectByteBuffer 对象作为这块内存的引用进行操作(详见: Java I/O 扩展), 这样就避免了在 Java堆和 Native 堆中来回复制数据, 因此在一些场景中可以显著提高性能。

4、G1 收集器

Garbage first 垃圾收集器是目前垃圾收集器理论发展的最前沿成果,相比与 CMS 收集器, G1 收集器两个最突出的改进是:

1、 基于标记-整理算法,不产生内存碎片。

2、 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间, 优先回收垃圾最多的区域。区域划分和优先级区域回收机制,确保 G1 收集器可以在有限时间获得最高的垃圾收集效率

5、堆的作用是什么?

是虚拟机所管理的内存中最大的一块,被所有线程共享的,在虚拟机启动时创建。堆用来存放对象实例,Java 里几乎所有对象实例都在堆分配内存。堆可以处于物理上不连续的内存空间,逻辑上应该连续,但对于例如数组这样的大对象,多数虚拟机实现出于简单、存储高效的考虑会要求连续的内存空间。

堆既可以被实现成固定大小,也可以是可扩展的,可通过 -Xms-Xmx 设置堆的最小和最大容量,当前主流 JVM 都按照可扩展实现。如果堆没有内存完成实例分配也无法扩展,抛出 OutOfMemoryError。

6、如何查看 JVM 当前使用的是什么垃圾收集器?

-XX:+PrintCommandLineFlags 参数可以打印出所选垃圾收集器和堆空间大小等设置

如果开启了 GC 日志详细信息,里面也会包含各代使用的垃圾收集器的简称

7、GC的回收流程是怎样的?

GC回收流程如下:

1、 对于整个的GC流程里面,那么最需要处理的就是新生代和老年代的内存清理操作,而元空间(永久代)都不在GC范围内

2、 当现在有一个新的对象产生,那么对象一定需要内存空间,平均每个栈内存存4k,每个堆内存存8k,那么对象一定需要进行堆空间的申请

3、 首先会判断Eden区是否有内存空间,如果此时有内存空间,则直接将新对象保存在伊甸园区。

4、 但是如果此时在伊甸园区内存不足,那么会自动执行一个Minor GC 操作,将伊甸园区的无用内存空间进行清理,Minor GC的清理范围只在Eden园区,清理之后会继续判断Eden园区的内存空间是否充足?如果内存空间充足,则将新对象直接在Eden园区进行空间分配。

5、 如果执行Minor GC 之后发现伊甸园区的内存空间依然不足,那么这个时候会执行存活区的判断,如果存活区有剩余空间,则将Eden园区部分活跃对象保存在存活区,那么随后继续判断Eden园区的内存空间是否充足,如果充足怎则将新对象直接在Eden园区进行空间分配。

6、 此时如果存活区没有内存空间,则继续判断老年区。则将部分存活对象保存在老年代,而后存活区将有空余空间。

7、 如果这个时候老年代也满了,那么这个时候将产生Major GC(Full GC),那么这个时候将进行老年代的清理

8、 如果老年代执行Full GC之后,无法进行对象的保存,则会产生OOM异常,OutOfMemoryError异常

8、类的实例化顺序

1、 父类静态成员和静态初始化块 ,按在代码中出现的顺序依次执行

2、 子类静态成员和静态初始化块 ,按在代码中出现的顺序依次执行

3、 父类实例成员和实例初始化块 ,按在代码中出现的顺序依次执行

4、 父类构造方法

5、 子类实例成员和实例初始化块 ,按在代码中出现的顺序依次执行

6、 子类构造方法

检验一下是不是真懂了:

public class Base {
    private String name = "博客:Soinice";

    public Base() {
        tellName();
        printName();
    }

    public void tellName() {
        System.out.println("Base tell name: " + name);
    }

    public void printName() {
        System.out.println("Base print name: " + name);
    }
}
public class Dervied extends Base {
    private String name = "Java3y";

    public Dervied() {
        tellName();
        printName();
    }

    @Override
    public void tellName() {
        System.out.println("Dervied tell name: " + name);
    }

    @Override
    public void printName() {
        System.out.println("Dervied print name: " + name);
    }

    public static void main(String[] args) {
        new Dervied();
    }
}

输出数据:

Dervied tell name: null
Dervied print name: null
Dervied tell name: Java3y
Dervied print name: Java3y

Process finished with exit code 0

第一次做错的同学点个赞,加个关注不过分吧(hahaha。

9、工作中常用的 JVM 配置参数有哪些?

Java 8 为例

日志

1、 -XX:+PrintFlagsFinal,打印JVM所有参数的值

2、 -XX:+PrintGC,打印GC信息

3、 -XX:+PrintGCDetails,打印GC详细信息

4、 -XX:+PrintGCTimeStamps,打印GC的时间戳

5、 -Xloggc:filename,设置GC log文件的位置

6、 -XX:+PrintTenuringDistribution,查看熬过收集后剩余对象的年龄分布信息

内存设置

1、 -Xms,设置堆的初始化内存大小

2、 -Xmx,设置堆的最大内存

3、 -Xmn,设置新生代内存大小

4、 -Xss,设置线程栈大小

5、 -XX:NewRatio,新生代与老年代比值

6、 -XX:SurvivorRatio,新生代中Eden区与两个Survivor区的比值,默认为8,即Eden:Survivor:Survivor=8:1:1

7、 -XX:MaxTenuringThreshold,从年轻代到老年代,最大晋升年龄。CMS 下默认为 6,G1 下默认为 15

8、 -XX:MetaspaceSize,设置元空间的大小,第一次超过将触发 GC

9、 -XX:MaxMetaspaceSize,元空间最大值

10、 -XX:MaxDirectMemorySize,用于设置直接内存的最大值,限制通过 DirectByteBuffer 申请的内存

11、 -XX:ReservedCodeCacheSize,用于设置 JIT 编译后的代码存放区大小,如果观察到这个值有限制,可以适当调大,一般够用即可

设置垃圾收集相关

1、 -XX:+UseSerialGC,设置串行收集器

2、 -XX:+UseParallelGC,设置并行收集器

3、 -XX:+UseConcMarkSweepGC,使用CMS收集器

4、 -XX:ParallelGCThreads,设置Parallel GC的线程数

5、 -XX:MaxGCPauseMillis,GC最大暂停时间 ms

6、 -XX:+UseG1GC,使用G1垃圾收集器

CMS 垃圾回收器相关

1、 -XX:+UseCMSInitiatingOccupancyOnly

2、 -XX:CMSInitiatingOccupancyFraction,与前者配合使用,指定MajorGC的发生时机

3、 -XX:+ExplicitGCInvokesConcurrent,代码调用 System.gc() 开始并行 FullGC,建议加上这个参数

4、 -XX:+CMSScavengeBeforeRemark,表示开启或关闭在 CMS 重新标记阶段之前的清除(YGC)尝试,它可以降低 remark 时间,建议加上

5、 -XX:+ParallelRefProcEnabled,可以用来并行处理 Reference,以加快处理速度,缩短耗时

G1 垃圾回收器相关

1、 -XX:MaxGCPauseMillis,用于设置目标停顿时间,G1 会尽力达成

2、 -XX:G1HeapRegionSize,用于设置小堆区大小,建议保持默认

3、 -XX:InitiatingHeapOccupancyPercent,表示当整个堆内存使用达到一定比例(默认是 45%),并发标记阶段就会被启动

4、 -XX:ConcGCThreads,表示并发垃圾收集器使用的线程数量,默认值随 JVM 运行的平台不同而变动,不建议修改

参数查询官网地址:

https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

建议面试时最好能记住 CMS 和 G1的参数,特点突出使用较多,被问的概率大

10、Java里有哪些引用类型?

1、 强引用 这种引用属于最普通最强硬的一种存在,只有在和 GC Roots 断绝关系时,才会被消灭掉。

2、 软引用 软引用用于维护一些可有可无的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。可以看到,这种特性非常适合用在缓存技术上。比如网页缓存、图片缓存等。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。

3、 弱引用 弱引用对象相比较软引用,要更加无用一些,它拥有更短的生命周期。当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。弱引用拥有更短的生命周期,在 Java 中,用 java.lang.ref.WeakReference 类来表示。它的应用场景和软引用类似,可以在一些对内存更加敏感的系统里采用。

4、 虚引用 这是一种形同虚设的引用,在现实场景中用的不是很多。虚引用必须和引用队列(ReferenceQueue)联合使用。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。实际上,虚引用的 get,总是返回 null。

11、本地方法区(线程私有)

12、什么是本地方法栈

13、栈溢出的原因?

14、Java的内存模型是什么?(JMM是什么?)

15、方法区溢出的原因?

16、JVM 监控与分析工具你用过哪些?介绍一下。

17、说说类加载的过程

18、MinorGC、MajorGC、FullGC 什么时候发生?

19、谈谈你知道的垃圾收集器

20、复制算法(copying)

21、垃圾收集算法

22、JVM的永久代中会发生垃圾回收么

23、类加载器双亲委派模型机制?

24、为什么需要双亲委派模式?

25、讲讲什么情况下会出现内存溢出,内存泄漏?

26、JVM 如何确定垃圾对象?

27、OSGI( 动态模型系统)

28、你平时工作中用过的JVM常用基本配置参数有哪些?

29、invokedynamic指令是干什么的?

30、程序计数器

31、强引用、软引用、弱引用、虚引用是什么?

用心去做高质量的内容网站,欢迎 star ⭐ 让更多人发现