Java虚拟机

一、类加载器 (类加载后存储于方法区(类区))
(1)类加载器有哪些
1、根类加载器(Bootstrap) –C++写的 ,看不到源码
2、扩展类加载器(Extension) –加载位置 :jre\lib\ext中
3、系统(应用)类加载器(System\App) –加载位置 :classpath中
4、自定义加载器(必须继承ClassLoader)

1、2是JDK相关类加载,3是系统依赖包加载,4是自定义加载。

(2)每个加载器都加载哪些类

  • BootstrapClassLoader:负责加载java基础类,主要是 %JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等
  • ExtensionClassLoader:负责加载java扩展类,主要是%JRE_HOME/lib/ext 目录下的jar和class
  • AppClassLoader:负责加载当前java应用的classpath中依赖包的所有类与本项目中的所有类。
  • Custom ClassLoader(自定义ClassLoader)

其中Bootstrap ClassLoader是JVM级别的,由C++撰写;ExtensionClassLoader、AppClassLoader都是java类,都继承自URLClassLoader超类。
Bootstrap ClassLoader由JVM启动,然后初始化sun.misc.Launcher ,sun.misc.Launcher初始化ExtensionClassLoader、App ClassLoader。

(3)加载类的父子关系
Bootstrap ClassLoader -》 ExtensionClassLoader -》 AppClassLoader

(4)为什么加载器使用双亲委派模型
因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义的类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader永远也无法加载一个自己写的String,除非你改变JDK中ClassLoader搜索类的默认算法。

(5)类加载过程是怎样的?
这里写图片描述
1) 装载:查找并加载类的二进制数据;
2)链接:
验证:确保被加载类的正确性;
准备:为类的静态变量分配内存,并将其初始化为默认值;
解析:把类中的符号引用转换为直接引用;
3)初始化:为类的静态变量赋予正确的初始值;
那为什么我要有验证这一步骤呢?首先如果由编译器生成的class文件,它肯定是符合JVM字节码格式的,但是万一有高手自己写一个class文件,让JVM加载并运行,用于恶意用途,就不妙了,因此这个class文件要先过验证这一关,不符合的话不会让它继续执行的,也是为了安全考虑吧。

准备阶段和初始化阶段看似有点矛盾,其实是不矛盾的,如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析,到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。

(6)如何定义自己的类加载器(个人感觉没什么意义,因为AppClassLoader已经加载完本项目的所有类)

package com.stream;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

/**
 * Created by moxianbin on 2017/8/13.
 */
public class TestClassLoader extends ClassLoader {

    private String rootUrl;

    public TestClassLoader(){}

    public TestClassLoader(String rootUrl){
        this.rootUrl = rootUrl;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz = null;

        byte[] classData = getClassData(name);  //根据类的二进制名称,获得该class文件的字节码数组
        if (classData == null) {
            throw new ClassNotFoundException();
        }
        clazz = defineClass(name, classData, 0, classData.length);  //将class的字节码数组转换成Class类的实例

        return clazz;
    }

    private byte[] getClassData(String name) {
        InputStream is = null;
        try {
            String path = classNameToPath(name);
            URL url = new URL(path);
            byte[] buff = new byte[1024*4];
            int len = -1;
            is = url.openStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            while((len = is.read(buff)) != -1) {
                baos.write(buff,0,len);
            }
            return baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch(IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    private String classNameToPath(String name) {
        return rootUrl + "/" + name.replace(".", "/") + ".class";
    }
}
@Test
    public void testClassLoader() throws Exception{
        System.out.println("==========Bootstrap ClassLoader是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库==========");
        java.net.URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for (int i = 0; i < urls.length; i++) {
            System.out.println(urls[i].toExternalForm());
        }
        System.out.println("==========寻找程序所需要的类==========");
        System.out.println("Bootstrap ClassLoader所需的包可以从sun.boot.class.path取得==" + System.getProperty("sun.boot.class.path"));
        System.out.println("Extension ClassLoader所需的包可以从java.ext.dirs取得==" + System.getProperty("java.ext.dirs"));
        System.out.println("App ClassLoader所需的包可以从java.class.path取得==" + System.getProperty("java.class.path"));

        System.out.println("==========查看父子关系==========");
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        System.out.println("App ClassLoader::getContextClassLoader==" + cl.toString());
        System.out.println("Extension ClassLoader::getContextClassLoader.parent==" + cl.getParent().toString());
        System.out.println("Bootstrap ClassLoader::getContextClassLoader.parent.parent==" + cl.getParent().getParent());

        System.out.println("==========自定义ClassLoader==========");
        ClassLoader testClassLoader = new TestClassLoader();
        Class clazz = testClassLoader.loadClass("com.ppmoney.appbe.controller.pojo.IntegrateTestEnvReq");
        System.out.println(clazz.getClassLoader().toString());
        System.out.println(clazz.getSimpleName());
    }
==========Bootstrap ClassLoader是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库==========
file:/C:/Program%20Files/Java/jdk1.8.0_66/jre/lib/resources.jar
file:/C:/Program%20Files/Java/jdk1.8.0_66/jre/lib/rt.jar
file:/C:/Program%20Files/Java/jdk1.8.0_66/jre/lib/sunrsasign.jar
file:/C:/Program%20Files/Java/jdk1.8.0_66/jre/lib/jsse.jar
file:/C:/Program%20Files/Java/jdk1.8.0_66/jre/lib/jce.jar
file:/C:/Program%20Files/Java/jdk1.8.0_66/jre/lib/charsets.jar
file:/C:/Program%20Files/Java/jdk1.8.0_66/jre/lib/jfr.jar
file:/C:/Program%20Files/Java/jdk1.8.0_66/jre/classes
==========寻找程序所需要的类==========
Bootstrap ClassLoader所需的包可以从sun.boot.class.path取得==C:\Program Files\Java\jdk1.8.0_66\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\rt.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\sunrsasign.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_66\jre\classes
Extension ClassLoader所需的包可以从java.ext.dirs取得==C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
App ClassLoader所需的包可以从java.class.path取得==C:\Program Files (x86)\JetBrains\IntelliJ IDEA 2016.2.1\lib\idea_rt.jar;C:\Program Files (x86)\JetBrains\IntelliJ IDEA 2016.2.1\plugins\junit\lib\junit-rt.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_66\jre\lib\rt.jar;C:\Users\moxianbin\git\gateway-test\gateway\target\test-classes;C:\Users\moxianbin\git\gateway-test\gateway\target\classes;D:\mavenrepo\com\whtr\dolphin\dolphin-server\0.7.34\dolphin-server-0.7.34.jar;D:\mavenrepo\com\whtr\dolphin\dolphin-core\0.7.34\dolphin-core-0.7.34.jar;D:\mavenrepo\com\whtr\dolphin\dolphin-contract\0.7.34\dolphin-contract-0.7.34.jar;D:\mavenrepo\com\google\guava\guava\19.0\guava-19.0.jar;D:\mavenrepo\org\slf4j\slf4j-api\1.7.21\slf4j-api-1.7.21.jar;D:\mavenrepo\ch\qos\logback\logback-classic\1.1.7\logback-classic-1.1.7.jar;D:\mavenrepo\org\springframework\boot\spring-boot-starter-jetty\1.4.1.RELEASE\spring-boot-starter-jetty-1.4.1.RELEASE.jar;D:\mavenrepo\org\eclipse\jetty\jetty-servlets\9.3.11.v20160721\jetty-servlets-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\jetty-continuation\9.3.11.v20160721\jetty-continuation-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\jetty-http\9.3.11.v20160721\jetty-http-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\jetty-util\9.3.11.v20160721\jetty-util-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\jetty-io\9.3.11.v20160721\jetty-io-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\jetty-webapp\9.3.11.v20160721\jetty-webapp-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\jetty-xml\9.3.11.v20160721\jetty-xml-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\jetty-servlet\9.3.11.v20160721\jetty-servlet-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\jetty-security\9.3.11.v20160721\jetty-security-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\jetty-server\9.3.11.v20160721\jetty-server-9.3.11.v20160721.jar;D:\mavenrepo\javax\servlet\javax.servlet-api\3.1.0\javax.servlet-api-3.1.0.jar;D:\mavenrepo\org\eclipse\jetty\websocket\websocket-server\9.3.11.v20160721\websocket-server-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\websocket\websocket-common\9.3.11.v20160721\websocket-common-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\websocket\websocket-api\9.3.11.v20160721\websocket-api-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\websocket\websocket-client\9.3.11.v20160721\websocket-client-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\websocket\websocket-servlet\9.3.11.v20160721\websocket-servlet-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\websocket\javax-websocket-server-impl\9.3.11.v20160721\javax-websocket-server-impl-9.3.11.v20160721.jar;D:\mavenrepo\org\eclipse\jetty\jetty-annotations\9.3.11.v20160721\jetty-annotations-9.3.11.v20160721.jar;
==========查看父子关系==========
App ClassLoader::getContextClassLoader==sun.misc.Launcher$AppClassLoader@14dad5dc
Extension ClassLoader::getContextClassLoader.parent==sun.misc.Launcher$ExtClassLoader@9629756
Bootstrap ClassLoader::getContextClassLoader.parent.parent==null
==========自定义ClassLoader==========
sun.misc.Launcher$AppClassLoader@15db9742
IntegrateTestEnvReq

Process finished with exit code 0

二、GC garbage collection

(1)什么时候触发GC

FullGC : 老年代的触发的GC,可回收老年代和Perm代
YoungGC : 年轻代的GC,又称为MinorGC
YoungGC 可能比较频繁一般多一些没关系,FullGC需要Hung住进程,发生多了影响响应时间,所以应该尽量避免。
可以通过设置-Xms(初始化内存大小)和-Xmx(最大内存大小)使堆定长,这样就会发生收缩和扩张,可以避免GC的发生。

首先需要知道,GC又分为YoungGC 和 Full Gc(也称为Major GC)。Java 堆内存分为新生代和老年代,新生代中又分为1个Eden区域 和两个 Survivor区域。

那么对于 Minor GC 的触发条件:大多数情况下,直接在 Eden 区中进行分配。如果 Eden区域没有足够的空间,那么就会发起一次 Minor GC;对于 Full GC(Major GC)的触发条件:也是如果老年代没有足够空间的话,那么就会进行一次 Full GC。

Ps:上面所说的只是一般情况下,实际上,需要考虑一个空间分配担保的问题:

在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。如果大于则进行Minor GC,如果小于则看HandlePromotionFailure设置是否允许担保失败(不允许则直接Full GC)。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于则尝试Minor GC(如果尝试失败也会触发Full GC),如果小于则进行Full GC。

但是,具体到什么时刻执行,这个是由系统来进行决定,是无法预测的。

(2)什么时候一个对象会被回收
主要根据可达性分析算法(又名根搜索法),如果一个对象不可达,那么就是可以回收的;如果一个对象可达,那么这个对象就不可以回收。对于可达性分析算法,它是通过一系列称为“GC Roots” 的对象作为起始点,当一个对象到 GC Roots 没有任何引用链相接的时候,那么这个对象就是不可达,就可以被回收。如下图的Obj5将会被回收:

这里写图片描述

这个GC Root 对象可以是一些静态的对象,Java方法的local变量或参数, native 方法引用的对象,活着的线程。

(3)GC做了什么事情,GC原理是什么。
主要做了清理对象,整理内存的工作。

GC原理:遍历内存中所有的对象 –> 找到那些你不在需要的(引用为null) –> 清理那块内存(不保证一定) –> 放入未使用的内存供其他地方用

这就是GC的大致流程,当然其中的很多不同的算法细节造就了不同的结构、效果:

    一、遍历对象,找到“垃圾”所使用的方法:

            *  引用计数法(经典,但是Sun Java未使用):
            引用计数很好理解,就是为每一个对象维护一个计数器,存储引用这个对象的个数,如:
            A a = new A();  // new出来的这个对象“X”的引用就为1
            A b = a ;  // “X”引用+1
            a = null;  // “X”引用-1
            当对象“X”的引用为0,说明没人再引用它,它就没用了。

            *   根搜索法(Sun Java使用):

            此算法中,所有的Java对象构成一颗近似“搜索树”的结构,有一个root根节点,每次从root出发向下搜索,当整个树遍历完成后,那些不在其中的变量则视为"垃圾"。
            如下对象可作为root可达的对象:
                    Java虚拟机栈中变量所引用的对象(比如A a = new A(),a即为栈中变量)  -- 最主要的
                    方法区中静态属性引用的对象
                    方法区中常量引用的对象
                    JNI Native方法引用的对象

参考:http://blog.csdn.net/ni357103403/article/details/51943379

(4)GC策略有哪几种

Java堆分为新生代和老年代,采用了不同的回收策略。例如新生代采用了复制算法,老年代采用了标记整理法。

a、标记-清除 算法(已不使用)
分为两个阶段:标记和清除,标记就是利用上述方法先找到所有人为是垃圾的对象,然后进入清除阶段,清理每块内存。是所有算法中最基本的,其他算法都是在它基础上演进的。可以看出它所存在的问题:

1、效率不高,遍历过程需要Hung住整个JVM(暂停进程执行)
2、会产生碎片,因为清理过程较简单,只是回收不会把不连续内存合并,有可能利用不了两块内存中间的空隙容量(如下图,灰色之间的白色区域不够新分配)

这里写图片描述

b、复制算法
在新生代中,分为一个Eden 区域和两个Survivor区域,真正使用的是一个Eden区域和一个Survivor区域,GC的时候,会把存活的对象放入到另外一个Survivor区域中,然后再把这个Eden区域和Survivor区域清除。
优点:可以避免碎片的问题,效率也不错
缺点:会浪费1/2的内存块,因为要作为buffer不能使用。所以这种方法不适合老年代这种大内存的地方,而且不适合长生命周期的对象,因为需要在两块内存之间拷贝多次。适合新生代这种比较小的内存块,不久之后将被回收,这就是就是S0/S1的实现方法。
这里写图片描述

c、标记-整理算法
那么对于老年代,采用的是标记整理法,首先标记出存活的对象,也就是上图的可达性分析算法,然后再移动到一端。这样也有利于减少内存碎片。
优点: 整理过程不需要另外一块内存buffer的参与,而且不会由于长时间存活的对象而造成频繁移动拷贝。所以适合老年代。

这里写图片描述

参考:http://blog.csdn.net/ni357103403/article/details/51943379
参考:http://blog.csdn.net/gugemichael/article/details/9345803

三、内存
(1)内存分为哪几个部分,分别存储什么数据
(2)对象从创建到销毁是怎么存活和转移的
(3)内存哪些部分会参与GC回收
(4)Java内存模型如何设计,为什么这么设计

数据结构相关的类实现原理

(1)HashMap是不是有序的?
答:不是,因为HashMap的基础数据结构是Node,每一个Node只记录自己的数据与位置
Node{
int hash;
V key;
V value;
}

(2)有没有有顺序的Map实现类?
有,LinkedHashMap 与 TreeMap

(3)LinkedHashMap 与 TreeMap怎么保证他们的顺序
a) LinkedHashMap的基础数据结构是Entry extend HashMap.Node
Entry{
Entry before;
Entry after;
}
即LinkedHashMap每个单元的Entry都记录了本身与前一个元素的数据(hashCode+Key+Value),即LinkedHashMap的本质是 双链表Node;

b) TreeMap 的实现是红黑树算法(需继续研究补充), 内部有个比较器 comparator,可以实现comparator接口实现对TreeMap中的Key排序,基础数据结构是Entry implements Map.Entry

Java并发包

(1)如果想实现所有的线程一起等待某个事件的发生,当某个事件发生时,所有线程一起开始往下执行的话,有什么好的办法吗

答: 栅栏(Java的并发包中的CyclicBarrier)

(2)你知道它的实现原理么?

(3)你还知道其他方式么

(4)你觉得这些方式里哪个方式更好

(5)如果你来实现,还有更好的方式么?

Java IO/NIO

(1)NIO模型是什么?

(2)selector的职责与实现原理

(3)NIO的核心是IO线程池,为什么

(4)IO包与NIO包的设计模式,为什么用装饰器模式,还有更好的设计模式了么?
答:装饰器模式

(5)NIO常用框架 Netty的实现原理或模型图

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐