- 类的实例化顺序
先静态、先父后子
先静态:父静态>子静态
优先级:父类>子类 静态代码块>非静态代码块>构造函数
一个类的实例化过程:
①.父类的static代码块,当前类的static;
②.顺序执行父类的普通代码块;
③.父类的构造函数;
④.子类的普通代码块;
⑤.子类(当前类)的构造函数,按顺序执行;
⑥.子类方法的执行;
根据jvm规范,jvm内存共分为虚拟机栈、堆、方法区、程序计数器和本地方法栈五个部分。
-
.方法区:主要存储类信息,常量池(static常量和static变量),编译后的代码(字节码)等信息;
-
.堆:初始化的对象,成员变量(非static变量),所有的对象实例和数组都要在堆上分配;
-
.栈:栈的结构是栈帧组成的,调用一个方法就压入一帧,帧上存储局部变量表,操作数栈,方法出口等信息,局部变量表存放的是八大基础类型加一个应用类型,所以还是一个指向地址的指针;
-
.程序技术器:记录当前线程执行的行号;
-
.本地方法栈:主要为Native方法服务。
Java8的内存分带改进
从永久代到元空间,在小范围自动拓展永生代避免溢出。
JVM垃圾回收机制,何时触发MinorGC等操作
分代垃圾回收机制:不同的对象生命周期不同。把不同生命周期的对象放在不同代上,不同代上采用最合适的垃圾回收方式进行回收。
JVM中共划分为三个代:年轻代、年老代和持久代。
年轻代:存放所有新生成的对象;
年老代:在年轻代中经历了N次垃圾回收仍然存活的对象,将被放在年老代中,故都是一些生命周期较长的对象;
持久代:用于存放静态文件,如Java类、方法等。
新生代的垃圾回收器命名为“Minor Gc”,老生代的垃圾回收器命名为“Full Gc 或 Major Gc”。其中在System.gc()强制执行的是Full Gc。
判断对象是否需要回收的两种方法:
①.引用计数
当某个对象的引用数为0时,便可以进行垃圾回收。
这种方法的最大缺点是无法检测出循环引用。如父对象有一个对子对象的引用,子对象反过来引用父对象。这样,它们的引用计数 永远也不可能为0.
②.可达性分析算法
可达性分析算法是从离散数学中的图论引入的,程序把所有的引用关系看做一张图,从一个节点GC ROOT开始,寻找对应的引用节点找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有引用的节点,即无用的节点,无用的节点将会被判定为垃圾回收的对象。
- 触发GC的条件
①.GC在优先级最低的线程中运行,一般在没有应用程序即没有应用线程在运行时被调用;
②.Java堆内存不足时,GC会被调用。
- JVM中完整的GC流程
①.对象优先在新生代中分配,如果内存不足,Minor GC;
②.大对象(需要大量连续内存空间)直接进入老年代;长期存活的对象进入老年代。如果对象在新生代经过一次 Minor GC回收仍然存活,年龄+1.若年龄超过一定限制(15),则晋升为老年代。
- 你知道哪几种垃圾回收器,各自的优缺点是什么?
Eden和Survivor的分配比例
默认比例8:1。大部分对象都是朝生夕死。
复制算法的基本思想就是将内存分为两块,每次只使用其中的一块,当这一块内存用完,就会将活着的对象复制到另一块上。复制算法不会产生内存碎片。
Java内存模型,以及在并发中的应用
Java内存模型的主要目标:定义程序中各个变量的访问规则。
Java 线程之间的通讯有java内存模型控制。
所有变量的存储都在主内存,每个线程都有自己的工作内存,线程的工作内存中保存了被线程引用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,不同的线程之间无法直接访问对方工作内存中的变量,线程之间变量的传递必须通过主内存来传递。
线程间的通讯:
①.首先,线程A把本地内存中更新的变量刷新到主内存中;
②.然后,线程B在主内存中读取线程A已经更新过的变量。
- volatile的语义,用volatile修饰的变量一定是线程安全吗?
一个变量被定义为volatile后,有两重含义:
①.保证此变量对所有线程的可见性,当一个线程修改了此变量,新值对于其它线程都是立即得知的,普通变量需要通过主内存来传递;
②.禁止指令的重排优化。
Valtile 修饰的变量不一定是线程安全的,eg非原子操作a++等。
对象之间的引用有哪些?
对象之间的引用主要有:强引用、弱引用、软引用和虚引用。
强引用:new出的对象之间的引用,只要强引用还在,就永远不会被回收;
弱引用:非必须的对象,对象能存活到下一次垃圾收集之前;
软引用:引用但非必须的对象,内存溢出异常之前回收;
虚引用:对生存时间无影响, 在垃圾回收事得到通知。