- 1.什么是字节码?使用字节码的好处是什么?
- 2.java类加载器有哪些
- 3.双亲委派模型
- 4.GC如何判断对象可以被回收
java中的编译器和解释器:
java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟的机器。这台机器在任何平台都提供给编译程序一个共同的接口。
编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在java中,这种供虚拟机理解的代码叫做字节码(.class文件),它不面向任何特定的处理器,只面向虚拟机。
每一种平台的解释器是不同的,但是实现的虚拟机是相同的。java源程序经过编译器编译后变成字节码,字节码由虚拟机解释运行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后再特定的机器上运行。这也解释了java的编译与解释并存的特点。
采用字节码的好处:
java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,java一次编译,处处运行。
- 启动类加载器(Bootstrap Class Loader): 负责加载存放在
lib目录,而且是java虚拟机能够识别的类库加载到虚拟机内存中,启动类加载器无法被java程序直接使用。 - 扩展类加载器(Extension Class Loader):负责加载
libext目录中的类。 - 应用程序类加载器(Application Class Loader):又叫系统类加载器,负责加载用户类路径(ClassPath)上所有的类库,一般情况下这个就是程序中默认的类加载器。
如图所展示的类加载器层次关系被称为双亲委派模型,除了顶层的启动类加载器外,其余的类加载器都有自己的父类加载器。不过这里类加载器之间的父子关系一般不是以继承来实现,而是使用组合关系复用父加载器的代码。
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父类反馈自己无法完成这个加载请求时,子类加载器才去尝试自己去完成加载。
简单来说,就是向上委派,向下查找。
优点:
- 避免类的重复加载
- 保护程序安全,防止核心API被随意篡改
- 引用计数法: 每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以被回收。
单纯的引用计数会出现循环引用的问题,所有java虚拟机并没有采用。 - 可达性分析法: 从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象不可能再被使用,虚拟机就判断是可回收对象。
GC Roots的对象有:
- 虚拟机栈中(栈帧中的本地变量表)引用的对象,如正在运行的方法所使用到的参数、局部变量、临时变量等。
- 在方法区中类静态属性引用的对象,譬如java类的引用类型静态变量。
- 在方法区中常量引用的对象,如字符串常量池里的引用。
- 在本地方法栈中JNI(即通常说的Native方法)引用的对象。
- 所有被同步锁synchronized持有的对象
判定为不可达的对象,并没有真正死亡,它只是被标记一次,随后判断此对象是否有必要执行finalize()方法(并不推荐)。
- 如果对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。
- 如果对象被判定有必要执行finalize()方法,那么该对象会被放置在一个名为F-Queue的队列中,并在稍后由一条由虚拟机自动建立的、低调度优先级的Finalizer线程去执行它们的finalize()方法,如果对象在finalize()中重新拯救自己–重新与引用链上的任何一个对象建立关系,它将被移出"即将回收"集合。



