07.Java类加载问题
代码案例如下所示
class A{ public static int value = 134; static{ System.out.println("A"); }}class B extends A{ static{ System.out.println("B"); }}public class Demo { public static void main(String args[]){ int s = B.value; System.out.println(s); }}
a.打印错误结果
A B134
b.打印正确结果
A134
观察代码,发现B.value中的value变量是A类的。所以,帮主在这里大胆的猜测一下,当遇到 类名.变量 加载时,只加载变量所在类。
如何做才能打印a这种结果呢?
class A{ public static int valueA = 134; static{ System.out.println("A"); }}class B extends A{ public static int valueB = 245; static{ System.out.println("B"); }}public class Demo { public static void main(String args[]){ int s = B.valueB; System.out.println(s); }}
得到数据技术博客大总结
AB245
7.0.0.5 看下面这段代码,说一下准备阶段和初始化阶段常量变化的原理?看下面这段代码
public static int value1 = 5;public static int value2 = 6;static{ value2 = 66;}
准备阶段和初始化阶段常量变化?结果在准备阶段value1和value2都等于0;在初始化阶段value1和value2分别等于5和66;变量初始化过程?所有类变量初始化语句和静态代码块都会在编译时被前端编译器放在收集器里头,存放到一个特殊的方法中,这个方法就是<clinit>方法,即类/接口初始化方法,该方法只能在类加载的过程中由JVM调用;编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量;如果超类还没有被初始化,那么优先对超类初始化,但在<clinit>方法内部不会显示调用超类的<clinit>方法,由JVM负责保证一个类的<clinit>方法执行之前,它的超类<clinit>方法已经被执行。JVM必须确保一个类在初始化的过程中,如果是多线程需要同时初始化它,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程。(所以可以利用静态内部类实现线程安全的单例模式)如果一个类没有声明任何的类变量,也没有静态代码块,那么可以没有类<clinit>方法;7.0.0.7 说收垃圾回收机制?为什么引用计数器判定对象是否回收不可行?判定对象可回收有两种方法:引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。然而在主流的Java虚拟机里未选用引用计数算法来管理内存,主要原因是它难以解决对象之间相互循环引用的问题,所以出现了另一种对象存活判定算法。可达性分析法:通过一系列被称为『GC Roots』的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。其中可作为GC Roots的对象:虚拟机栈中引用的对象,主要是指栈帧中的本地变量、本地方法栈中Native方法引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象回收算法有以下四种:分代收集算法:是当前商业虚拟机都采用的一种算法,根据对象存活周期的不同,将Java堆划分为新生代和老年代,并根据各个年代的特点采用最适当的收集算法。技术博客大总结新生代:大批对象死去,只有少量存活。使用『复制算法』,只需复制少量存活对象即可。老年代:对象存活率高。使用『标记—清理算法』或者『标记—整理算法』,只需标记较少的回收对象即可。复制算法:把可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用尽后,把还存活着的对象『复制』到另外一块上面,再将这一块内存空间一次清理掉。标记-清除算法:首先『标记』出所有需要回收的对象,然后统一『清除』所有被标记的对象。标记-整理算法:首先『标记』出所有需要回收的对象,然后进行『整理』,使得存活的对象都向一端移动,最后直接清理掉端边界以外的内存。垃圾收集算法分类标记-清楚算法(Mark-Sweep)在标记阶段,确定所有要回收的对象,并做标记。清除阶段紧随标记阶段,将标记阶段确定不可用的对象清除。标记—清除算法是基础的收集算法,有两个不足:1)标记和清除阶段的效率不高;2)清除后回产生大量的不连续空间,这样当程序需要分配大内存对象时,可能无法找到足够的连续空间。复制算法(Copying)复制算法是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另一块上,然后把这块内存整个清理掉。复制算法实现简单,运行效率高,但是由于每次只能使用其中的一半,造成内存的利用率不高。现在的JVM 用复制方法收集新生代,由于新生代中大部分对象(98%)都是朝生夕死的,所以会分成1块大内存Eden和两块小内存Survivor(大概是8:1:1),每次使用1块大内存和1块小内存,当回收时将2块内存中存活的对象赋值到另一块小内存中,然后清理剩下的。标记—整理算法(Mark-Compact)标记—整理算法和复制算法一样,但是标记—整理算法不是把存活对象复制到另一块内存,而是把存活对象往内存的一端移动,然后直接回收边界以外的内存。标记—整理算法提高了内存的利用率,并且它适合在收集对象存活时间较长的老年代。分代收集(Generational Collection)分代收集是根据对象的存活时间把内存分为新生代和老年代,根据各代对象的存活特点,每个代采用不同的垃圾回收算法。新生代采用复制算法,老年代采用标记—整理算法。为什么引用计数器判定对象是否回收不可行?实现简单,判定效率高,但不能解决循环引用问题,同时计数器的增加和减少带来额外开销。引用类型有哪些种强引用:默认的引用方式,不会被垃圾回收,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。软引用(SoftReference):如果一个对象只被软引用指向,只有内存空间不足够时,垃圾回收器才会回收它;弱引用(WeakReference):如果一个对象只被弱引用指向,当JVM进行垃圾回收时,无论内存是否充足,都会回收该对象。虚引用(PhantomReference):虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。虚引用通常和ReferenceQueue配合使用。7.0.0.8 谈谈Java的类加载过程?加载做了什么?验证做了什么?准备做了什么?解析做了什么?初始化做了什么?Java文件从编码完成到最终执行过程编译:编译,即把我们写好的java文件,通过javac命令编译成字节码,也就是我们常说的.class文件。运行:运行,则是把编译声称的.class文件交给Java虚拟机(JVM)执行。举个通俗点的例子来说,JVM在执行某段代码时,遇到了classA,然而此时内存中并没有classA的相关信息,于是JVM就会到相应的class文件中去寻找classA的类信息,并加载进内存中,这就是我们所说的类加载过程。谈谈Java的类加载过程?类加载的过程主要分为三个部分:加载链接而链接又可以细分为三个小部分:验证准备解析初始化加载做了什么?加载指的是把class字节码文件从各个来源通过类加载器装载入内存中。这里有两个重点:字节码来源。一般的加载来源包括从本地路径下编译生成的.class文件,从jar包中的.class文件,从远程网络,以及动态代理实时编译类加载器。一般包括启动类加载器,扩展类加载器,应用类加载器,以及用户的自定义类加载器。在加载阶段(可以参考java.lang.ClassLoader的loadClass()方法),虚拟机需要完成以下3件事情:通过一个类的全限定名来获取定义此类的二进制字节流(并没有指明要从一个Class文件中获取,可以从其他渠道,譬如:网络、动态生成、数据库等);将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口;加载阶段和连接阶段(Linking)的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始,但这些夹在加载阶段之中进行的动作,仍然属于连接阶段的内容,这两个阶段的开始时间仍然保持着固定的先后顺序。验证做了什么?技术博客大总结主要是为了保证加载进来的字节流符合虚拟机规范,不会造成安全错误。包括对于文件格式的验证,比如常量中是否有不被支持的常量?文件中是否有不规范的或者附加的其他信息?对于元数据的验证,比如该类是否继承了被final修饰的类?类中的字段,方法是否与父类冲突?是否出现了不合理的重载?对于字节码的验证,保证程序语义的合理性,比如要保证类型转换的合理性。对于符号引用的验证,比如校验符号引用中通过全限定名是否能够找到对应的类?校验符号引用中的访问性(private,public等)是否可被当前类访问?准备做了什么?主要是为类变量(注意,不是实例变量)分配内存,并且赋予初值。特别需要注意,初值,不是代码中具体写的初始化的值,而是Java虚拟机根据不同变量类型的默认初始值。比如8种基本类型的初值,默认为0;引用类型的初值则为null;常量的初值即为代码中设置的值,final static a = 123, 那么该阶段a的初值就是123解析做了什么?将常量池内的符号引用替换为直接引用的过程。两个重点:符号引用。即一个字符串,但是这个字符串给出了一些能够唯一性识别一个方法,一个变量,一个类的相关信息。直接引用。可以理解为一个内存地址,或者一个偏移量。比如类方法,类变量的直接引用是指向方法区的指针;而实例方法,实例变量的直接引用则是从实例的头指针开始算起到这个实例变量位置的偏移量举个例子来说,现在调用方法hello(),这个方法的地址是1234567,那么hello就是符号引用,1234567就是直接引用。在解析阶段,虚拟机会把所有的类名,方法名,字段名这些符号引用替换为具体的内存地址或偏移量,也就是直接引用。初始化做了什么?这个阶段主要是对类变量初始化,是执行类构造器的过程。换句话说,只对static修饰的变量或语句进行初始化。如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。其他介绍01.关于博客汇总链接1.技术博客汇总2.开源项目汇总3.生活博客汇总4.喜马拉雅音频汇总5.其他汇总02.关于我的博客我的个人站点:www.yczbj.org,www.ycbjie.cngithub:https://github.com/yangchong211知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts简书:http://www.jianshu.com/u/b7b2c6ed9284csdn:http://my.csdn.net/m0_37700275喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/开源中国:https://my.oschina.net/zbj1618/blog泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1邮箱:yangchong211@163.com阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcVsegmentfault头条:https://segmentfault.com/u/xiangjianyu/articles掘金:https://juejin.im/user/5939433efe88c2006afa0c6e
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。