Java内存模型

物理硬件高速缓存

    1.概念:将运算需要使用到的数据复制到缓存中,让运算能快速运行,当运算结束后再从缓存同步回内存之中,这样处理器就无须等待处理缓慢的内存读写了。

(更多…)

03-类加载器

类加载器

    
 
    类加载定义:通过一个类的全限定名来获取描述此类的二进制流来获取所需要的类的动作
        
    类从被加载到虚拟机内存中开始,到卸载出内存生命周期分为以下7个阶段
        加载(Loading) -> 【验证(Verification) -> 准备(Preparation) -> 解析(Resolution)】 -> 初始化(Initialization) -> 使用(Using) -> 卸载(Unloading)
        验证、准备和解析统称为连接(Linking)
    
    主动引用(会触发类初始化的引用):
        1.遇到new,getstatic,putstatic或invokestatic这4条指令时,若类没有初始化会被触发初始化(new对象时、读取或者设置类静态非常量字段时、调用类静态方法)
        2.反射调用类时
        3.初始化类,其父类还未被初始化时触发父类初始化
        4.虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的那个类),虚拟机会先初始化这个类
    被动引用(不会触发类初始化的引用):
        1.通过子类引用父类的静态字段
        2.通过定义数组来引用类
        3.在A类中用到了B类的静态常量,这里B类不会被初始化。常量在编译阶段会存到调用类的常量池中,本质上没有直接调用定义常量的类
    
    双亲委派模型:
        1.虚拟机的角度看类加载器种类:

                a.启动类加载器(Bootstrap ClassLoader),用C++实现。
                b.所有的其他类加载器,都由Java实现。并且都继承于java.lang.ClassLoader类
        2.开发人员的角度看类加载器种类:
                a.启动类加载器(Bootstrap ClassLoader):负责加载<JAVA_HOME>/lib目录下。(按照文件名识别)
                b.扩展类加载器(Extension ClassLoader):这个类加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中所有的类库
                c.应用程序类加载器(Application ClassLoader):这个类加载器由sun.misc.Launcher$AppClassLoader实现,由于ClassLoader的getSystemClassLoader方法返回值也是它,所以一般它也被称为系统加载器。
 
        工作过程:
            如果一个类收到了类加载的请求,它首先不会自己去尝试去加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此。
    因此所有的类加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
    
       双亲委派模型带来的好处:
            1.[稳定性]同一个类始终会给同一个类加载器去加载,例如java.lang.Object在rt.jar中。那么在各种类加载器环境中都会是委派给启动类加载器,所以系统中也只会有一个Objec类
    
 
         ClassLoader实现双亲委派模型源码:
/**
*使用指定的二进制名称来加载类。此方法的默认实现将按以下顺序搜索类: 
1.调用 findLoadedClass(String) 来检查是否已经加载类。
2.在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。 
3.调用 findClass(String) 方法查找类。
如果使用上述步骤找到类,并且 resolve 标志为真,则此方法将在得到的 Class 对象上调用 resolveClass(Class) 方法。 
鼓励用 ClassLoader 的子类重写 findClass(String),而不是使用此方法,ClassLoader的findClass方法默认是抛出ClassNotFoundException

**/ 
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        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;
        }
    }

02-Reference & GC

一、引用
    Java中引用的定义很传统:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。这个定义很纯粹,但是太过狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态,对于如何描述一些“食之无味弃之可惜”的对象就显得无能为力。我们希望能描述这样一类对象:当内存空间还足够时,则能保存在内存中;如果内存在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。
    在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为:
        以下四种引用强度依次逐渐减弱
        1.强引用(Strong Reference)
            强引用就是指在程序代码中普遍存在的,类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
        2.软引用(Soft Reference)
            软引用用来描述一些还有用,但并非必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,会把这些对象列进回收范围之中并进行第二次回收。如果这次回收还是没有足够的内存,才会抛出内存溢出异常。在JDK1.2之后,提供了SoftReference类来实现软引用。
        3.弱引用(Weak Reference)
            弱引用也是用来描述非必须对象的,但是它的强度比弱引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集器发生之前。当垃圾收集器工作时,无论当前的内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用。
        4.虚引用(Phantom Reference)
            虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回收时收到一个系统通知。在JDK1.2之后,提供了PhantomReference来实现虚引用。
二、判断对象已死算法
    1.引用计数算法(Reference Counting)
        定义:给对象中添加一个引用计数器,每当有个地方引用它时,计算器值就加一;当引用失效时,计数器值就减1;任何时候计算器都为0的对象就是不可能再被使用的。
        客观地说,引用计数算法的实现简单,判定效率也很高,在大部分情况下它都是一个不错的算法,但是Java中没有选用它来管理内存,其中最主要的原因是它很难解决对象之间的相互循环引用的问题
    2.根搜索算法(GC Roots Tracing)
        基本思路:通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始往下搜索,搜索走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
        在Java语言里,可作为GC Roots的对象包括以下几种:
        1.虚拟机栈(栈帧中的本地变量表)中引用的对象。
        2.方法区中的类静态属性引用的对象。
        3.方法区中的常量引用的对象。
        4.本地方法栈JNI(即一般说的Native方法)的引用的对象。
三、对象死亡的两次标记过程
    在根搜索算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经过两次标记过程:如果对象在进行根搜索后发现没有与GC Roots相连接的引用链,那它会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize方法或者finalize已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。
    经过上面的判定如果有必要执行finalize方法,那么这个对象将会被放置在一个名为F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这样做的原因是,如果一个对象在finalize方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。finalize方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize中拯救自己–只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类的变量或对象的成员变量,那在第二次标记时它将被移