本文共 8237 字,大约阅读时间需要 27 分钟。
目录介绍
- 7.0.0.1 Java内存模型里包含什么?程序计数器的作用是什么?常量池的作用是什么?
- 7.0.0.2 什么是类加载器?类加载器工作机制是什么?类加载器种类?什么是双亲委派机制?
- 7.0.0.3 什么时候发生类初始化?类初始化后对类的做了什么,加载变量,常量,方法都内存那个位置?
- 7.0.0.4 通过下面一个代码案例理解类加载顺序?当遇到 类名.变量 加载时,只加载变量所在类吗?
- 7.0.0.5 看下面这段代码,说一下准备阶段和初始化阶段常量变化的原理?变量初始化过程?
- 7.0.0.7 说收垃圾回收机制?为什么引用计数器判定对象是否回收不可行?有哪些引用类型?
- 7.0.0.8 谈谈Java的类加载过程?加载做了什么?验证做了什么?准备做了什么?解析做了什么?初始化做了什么?
好消息
- 博客笔记大汇总【15年10月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计500篇[近100万字],将会陆续发表到网上,转载请注明出处,谢谢!
- 链接地址:
- 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!
7.0.0.1 Java内存模型里包含什么?程序计数器的作用是什么?常量池的作用是什么?
-
Java内存模型里包含什么?
- JVM会用一段空间来存储执行程序期间需要用到的数据和相关信息,这段空间就是运行时数据区(Runtime Data Area),也就是常说的JVM内存。JVM会将它所管理的内存划分为线程私有数据区和线程共享数据区两大类。
-
线程私有数据区包含:
- 1.程序计数器:是一个数据结构,用于保存当前正常执行的程序的内存地址。Java虚拟机的多线程就是通过线程轮流切换并分配处理器时间来实现的,为了线程切换后能恢复到正确的位置,每条线程都需要一个独立的程序计数器,互不影响,该区域为“线程私有”。
- 2.Java虚拟机栈:线程私有的,与线程生命周期相同,用于存储局部变量表,操作栈,方法返回值。局部变量表放着基本数据类型,还有对象的引用。
- 3.本地方法栈:跟虚拟机栈很像,不过它是为虚拟机使用到的Native方法服务。
- 线程共享数据区包含:
-
- 4.Java堆:所有线程共享的一块内存区域,用于存放几乎所有的对象实例和数组;是垃圾收集器管理的主要区域,也被称做“GC堆”;是Java虚拟机所管理的内存中最大的一块。
- 5.方法区:各个线程共享的区域,储存虚拟机加载的类信息,常量,静态变量,编译后的代码。
- 6.运行时常量池:代表运行时每个class文件中的常量表。包括几种常量:编译时的数字常量、方法或者域的引用。
- 程序计数器的作用是什么?
- 常量池的作用是什么?
7.0.0.2 什么是类加载器?类加载器工作机制是什么?类加载器种类?什么是双亲委派机制?
-
什么是类加载器?
- 负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例;
-
类加载器工作机制是什么
-
类加载器种类?
- 启动类加载器,Bootstrap ClassLoader,加载JACA_HOMElib,或者被-Xbootclasspath参数限定的类
- 扩展类加载器,Extension ClassLoader,加载libext,或者被java.ext.dirs系统变量指定的类
- 应用程序类加载器,Application ClassLoader,加载ClassPath中的类库
- 自定义类加载器,通过继承ClassLoader实现,一般是加载我们的自定义类
-
什么是双亲委派机制?
-
主要是表示类加载器之间的层次关系
- 前提:除了顶层启动类加载器外,其余类加载器都应当有自己的父类加载器,且它们之间关系一般不会以继承(Inheritance)关系来实现,而是通过组合(Composition)关系来复用父加载器的代码。
- 工作过程:若一个类加载器收到了类加载的请求,它先会把这个请求委派给父类加载器,并向上传递,最终请求都传送到顶层的启动类加载器中。只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
7.0.0.3 什么时候发生类初始化?类初始化后对类的做了什么,加载变量,常量,方法都内存那个位置?
-
什么时候发生类初始化
-
类初始化后对类的做了什么
- 这个阶段主要是对类变量初始化,是执行类构造器的过程。
- 换句话说,只对static修饰的变量或语句进行初始化。
- 如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
- 如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。
7.0.0.4 通过下面一个代码案例理解类加载顺序?当遇到 类名.变量 加载时,只加载变量所在类吗?
-
代码案例如下所示
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 看下面这段代码,说一下准备阶段和初始化阶段常量变化的原理?
7.0.0.7 说收垃圾回收机制?为什么引用计数器判定对象是否回收不可行?
-
判定对象可回收有两种方法:
-
引用计数算法:
- 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。然而在主流的Java虚拟机里未选用引用计数算法来管理内存,主要原因是它难以解决对象之间相互循环引用的问题,所以出现了另一种对象存活判定算法。
-
可达性分析法:
- 通过一系列被称为『GC Roots』的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。其中可作为GC Roots的对象:虚拟机栈中引用的对象,主要是指栈帧中的本地变量、本地方法栈中Native方法引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象
-
回收算法有以下四种:
-
垃圾收集算法分类
-
为什么引用计数器判定对象是否回收不可行?
- 实现简单,判定效率高,但不能解决循环引用问题,同时计数器的增加和减少带来额外开销。
-
引用类型有哪些种
- 强引用:默认的引用方式,不会被垃圾回收,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的类加载过程?
- 类加载的过程主要分为三个部分:
- 加载
-
链接
- 初始化
-
加载做了什么?
-
验证做了什么?
- 主要是为了保证加载进来的字节流符合虚拟机规范,不会造成安全错误。
- 包括对于文件格式的验证,比如常量中是否有不被支持的常量?文件中是否有不规范的或者附加的其他信息?
- 对于元数据的验证,比如该类是否继承了被final修饰的类?类中的字段,方法是否与父类冲突?是否出现了不合理的重载?
- 对于字节码的验证,保证程序语义的合理性,比如要保证类型转换的合理性。
- 对于符号引用的验证,比如校验符号引用中通过全限定名是否能够找到对应的类?校验符号引用中的访问性(private,public等)是否可被当前类访问?
-
准备做了什么?
- 主要是为类变量(注意,不是实例变量)分配内存,并且赋予初值。
- 特别需要注意,初值,不是代码中具体写的初始化的值,而是Java虚拟机根据不同变量类型的默认初始值。
- 比如8种基本类型的初值,默认为0;引用类型的初值则为null;常量的初值即为代码中设置的值,final static a = 123, 那么该阶段a的初值就是123
-
解析做了什么?
- 将常量池内的符号引用替换为直接引用的过程。
-
两个重点:
- 符号引用。即一个字符串,但是这个字符串给出了一些能够唯一性识别一个方法,一个变量,一个类的相关信息。
- 直接引用。可以理解为一个内存地址,或者一个偏移量。比如类方法,类变量的直接引用是指向方法区的指针;而实例方法,实例变量的直接引用则是从实例的头指针开始算起到这个实例变量位置的偏移量
- 举个例子来说,现在调用方法hello(),这个方法的地址是1234567,那么hello就是符号引用,1234567就是直接引用。
- 在解析阶段,虚拟机会把所有的类名,方法名,字段名这些符号引用替换为具体的内存地址或偏移量,也就是直接引用。
-
初始化做了什么?
- 这个阶段主要是对类变量初始化,是执行类构造器的过程。
- 换句话说,只对static修饰的变量或语句进行初始化。
- 如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
- 如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。
其他介绍
01.关于博客汇总链接
02.关于我的博客
- 我的个人站点:www.yczbj.org,www.ycbjie.cn
- github:
- 知乎:
- 简书:
- csdn:
- 喜马拉雅听书:
- 开源中国:
- 泡在网上的日子:
- 邮箱:yangchong211@163.com
- 阿里云博客: 239.headeruserinfo.3.dT4bcV
- segmentfault头条:
- 掘金: