本文介绍JVM中的几个面试题,十分有用
主要有几题

简单来说,可以这样理解分类
.class文件加载至JVM中int是0,包装类为null)什么是符号引用,什么又是直接引用 可以这样进行理解,我们有一个
A类和B类,A类中使用到了B类 在字节码中,会用一个符号代表这是B类,这就是符号引用 而在B类进行类加载后,JVM成功的加载了这个B类,使得堆内存中有对应的B.class的对象,同时方法区中有静态方法与属性。 这个时候,A类就会将之前的符号引用,改为直接引用,设置为上面堆内存的B.class对象,或者方法区中的静态方法与属性类加载的时机
在了解双亲委派机制之前,我们先设想一个问题,就是如果我们用户自己写一个String这样一个的类,会出现什么样的情况?
这个问题说简单也简单,说复杂就比较复杂了,这个问题正好是由双亲委派机制来进行解决的。
在了解双亲委派机制之前,我们先得了解几个ClassLoader类加载器
类加载器 | 说明 | 加载类的范围 |
|---|---|---|
Bootstrap ClassLoader | 启动类加载器,最顶层的类加载器,这个加载器,Java中不能获取,返回的是一个null | <JAVA HOME>/lib |
Extension ClassLoader | 扩展类加载器 | <JAVA HOME>/lib/ext |
Application ClassLoader | 应用程序类加载器,也是我们最常用的类加载器 | classpath/java.class.path |
User ClassLoader | 用户自定义的类加载器 | 任意来源的类 |
好的,当了解完上面的四种类加载器之后,我们将进行验证,看下面代码
package com.banmoon.parentsappoint;
public class ParentsAppointTest {
public static void main(java.lang.String[] args) {
System.out.println("java.lang.String:" + "abc".getClass().getClassLoader());
System.out.println("com.banmoon.parentsappoint.String:" + String.class.getClassLoader());
}
}
为什么,他们的类加载器是不同的呢。有人说了,是因为类加载器本身就是有不同的加载类职责范围。
那么当我们进行类加载的时候,程序怎么知道这个类要用什么类加载器。然而就是这段不同的类,确定使用不同类加载器的过程,就是我们将的双亲委派机制。
我们先看这段代码,正是双亲委派机制的代码,在ClassLoader.java中可以找到这段代码
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先,先检查类是否已经被加载
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// parent是父亲加载器,这里仅仅是逻辑层面上的,并不是指继承方面的父类
if (parent != null) {
// 如果父亲加载器不为空,则先交给父亲加载器进行类加载
c = parent.loadClass(name, false);
} else {
// 如果父亲加载器为空,那说明接下来是Bootstrap ClassLoader了,直接交给特殊的方法进行加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
// 如果上面的父亲类加载器没有加载成功,那就自己查找
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}这就是双亲委派机制,下面可以画一个图

可以看到,类加载的时候,永远都是Bootstrap ClassLoader(启动类加载器)尝试去加载
当然,如果类不合适,将会向下进行委派加载
上面的这种行为可以这样概括,向上检查,向下委托加载
下面这个就是JVM的内存模型,有些细节没有完全画出来,后续会补上

需要讲一下,其中的这些是什么意思
String的属性,那么在类加载的连接阶段,常量池中会存储这么一个指针常量。
Class对象。还有就是开发者编写的静态变量。GC的主战场。下面篇幅会提到
Java方法栈,这样好理解一下。Java在调用方法时,会将字节码方法入栈,这个东西叫做栈帧。栈这种数据结构,就是先入后出。类似的,一个A方法压入栈,这个方法调用一个B方法,就会将B方法压入栈。结构展示A在最底下,B在上。在结束的时候,是B方法栈帧先结束,然后才是A方法的栈帧。符合先入后出原则。在栈帧结构内部,我们可以如下进行划分,分别是
Java是由C++语言编写的,里面肯定会调用到C++,故本地方法栈就是存储的是调用C++方法时的变量存储。
我是半月,你我一同共勉!!!