文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820…
种一棵树最好的时间是十年前,其次是现在
我知道很多人不玩qq了,但是怀旧一下,欢迎加入六脉神剑Java菜鸟学习群,群聊号码:549684836 鼓励大家在技术的路上写博客
学Java的没办法都逃不过Java虚拟机的,所以这个系列是必须讲的,因为你要构建你的Java知识体系,你就肯定知道要怎么按步骤学,对于JVM的学习我也自己的一个小规划吧,从JVM介绍,然后,Java文件编程成.class文件,然后Java虚拟机怎么加载这写.class文件,加载到虚拟机之后,这些数据怎么再Java虚拟机中存储,存储之后我们知道Java是自动回收垃圾,不像C C++那样,那我们我们肯定得知道垃圾回收算法,和垃圾回收器,最后到真正的一个Java系统的JVM调优,这个就是我打算要讲的这个系列,然后我大多数类容参考周志明老师的深入理解Java虚拟机
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行 ,是Java跨平台的原因。
这个是我从oracle官网截图的,从中可以看出JDK=JRE+一些工具,JRE里面包含了JVM(Java虚拟机)
JVM总体上是由
以上5个部分组成,每一个都是非常重要的,如果你要了解JVM,要学习JVM调优,那么只能是一个个去把他们啃了
书上的原话:
虚拟机把描述类的数据从Class文件加载到内存,并对这些数据进行校验,转换 解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括
总共是7个阶段
首先 类 是指的.Class文件类,那么怎么生成这个文件呢?
等等 还有很多
那么 加载 这2个字应该怎么理解呢 大家可以看下图
本地的.Class文件通过类加载器加载到JVM内存中的方法区里面,然后通过这个对象来访问数据区的数据
Java并没用规定生命时候进行类加载的第一阶段,但是对于初始化阶段,虚拟机有严格的规范
分为以下几种样装情况
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。对于该阶段有以下几点需要注意:
1、这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。
2、这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。
假设一个类变量的定义为:
public static int value = 3;
那么变量value在准备阶段过后的初始值为0,而不是3,因为这时候尚未开始执行任何Java方法,而把value赋值为3的putstatic指令是在程序编译后,存放于类构造器()方法之中的,所以把value赋值为3的动作将在初始化阶段才会执行。
下表列出了Java中所有基本数据类型以及reference类型的默认零值:
这里还需要注意如下几点:
如果类字段的字段属性表中存在ConstantValue属性,即同时被final和static修饰,那么在准备阶段变量value就会被初始化为ConstValue属性所指定的值。
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程 ##初始化阶段 到了初始化阶段,才真正开始执行类中定义的Java代码
站在Java虚拟机的角度来讲,只存在两种不同的类加载器:
站在Java开发人员的角度来看,类加载器可以大致划分为以下四类:
这几种类加载器的层次关系如下图所示:
类加载器之间的这种层次关系叫做双亲委派模型。 双亲委派模型要求除了顶层的启动类加载器(Bootstrap ClassLoader)外,其余的类加载器都应当有自己的父类加载器。这里的类加载器之间的父子关系一般不是以继承关系实现的,而是用组合实现的。
由我来概况就是 八个字 向上检查,从下加载
如果一个类接受到类加载请求,他自己不会去加载这个请求,而是将这个类加载请求委派给父类加载器,这样一层一层传送,直到到达启动类加载器(Bootstrap ClassLoader)。
只有当父类加载器无法加载这个请求时,子加载器才会尝试自己去加载。
双亲委派模型的代码实现集中在java.lang.ClassLoader的loadClass()方法当中。
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { //1 首先检查类是否被加载 Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { //2 没有则调用父类加载器的loadClass()方法; c = parent.loadClass(name, false); } else { //3 若父类加载器为空,则默认使用启动类加载器作为父加载器; c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { //4 若父类加载失败,抛出ClassNotFoundException 异常后,这个方法就是加载的核心代码 c = findClass(name); } } if (resolve) { //5 再调用自己的findClass() 方法。 resolveClass(c); } return c; } 复制代码
class NetworkClassLoader extends ClassLoader { * String host; * int port; * * public Class findClass(String name) { * byte[] b = loadClassData(name); * return defineClass(name, b, 0, b.length); * } * * private byte[] loadClassData(String name) { * // load the class data from the connection * . . . * } * } 复制代码
这个就是官方的例子
双亲委派模型很好的解决了各个类加载器加载基础类的统一性问题。即越基础的类由越上层的加载器进行加载。 若加载的基础类中需要回调用户代码,而这时顶层的类加载器无法识别这些用户代码,怎么办呢?这时就需要破坏双亲委派模型了。
java默认的线程上下文类加载器是系统类加载器(AppClassLoader).
// Now create the class loader to use to launch the application try { loader = AppClassLoader.getAppClassLoader(extcl); } catch (IOException e) { throw new InternalError( "Could not create application class loader" ); } // Also set the context class loader for the primordial thread. Thread.currentThread().setContextClassLoader(loader); 复制代码
以上代码摘自sun.misc.Launch的无参构造函数Launch()。
使用线程上下文类加载器,可以在执行线程中,抛弃双亲委派加载链模式,使用线程上下文里的类加载器加载类.
典型的例子有,通过线程上下文来加载第三方库jndi实现,而不依赖于双亲委派.
大部分java app服务器(jboss, tomcat..)也是采用contextClassLoader来处理web服务。
今天把类加载机制好好讲了一下,这样大家就更加的熟悉了内的加载过程,对于Java开发是有好处的
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是真粉。
创作不易,各位的支持和认可,就是我创作的最大动力,我们下篇文章见
六脉神剑 | 文 【原创】如果本篇博客有任何错误,请批评指教,不胜感激 !