学习笔记,自用,整理中
面
JVM内存结构说一下!
开放式题目,具体可见章节运行时数据区域
一般从两个维度出发:线程私有和线程共享。到每一个内存区域的细节点。
Java 虚拟机栈是基于线程的。哪怕你只有一个 main() 方法,也是以线程的方式运行的。在线程的生命周期中,参与计算的数据会频繁地入栈和出栈,栈的生命周期是和线程一样的。
栈里的每条数据,就是栈帧。在每个Java 方法被调用的时候,都会创建一个栈帧,并入栈。一旦完成相应的调用,则出栈。所有的栈帧都出栈后,线程也就结束了。每个栈帧,都包含四个区域:
局部变量表
操作数栈
动态连接
返回地址
本地方法栈是和虚拟机栈非常相似的一个区域,它服务的对象是native 方法。
程序计数器是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。
堆是JVM 上最大的内存区域,我们申请的几乎所有的对象,都是在这里存储的。我们常说的垃圾回收,操作的对象就是堆。
方法区,这个区域存储的内容,包括:类的信息、常量池、方法数据、方法代码就可以了。
什么情况下内存栈溢出?
java.lang.StackOverflowError 如果出现了可能会是无限递归。
OutOfMemoryError:不断建立线程,JVM申请栈内存,机器没有足够的内存。
描述new一个对象的流程!
具体见章节对象的分配
Java对象会不会分配在栈中?
可以,如果这个对象不满足逃逸分析,那么虚拟机在特定的情况下会走栈上分配。
如果判断一个对象是否被回收,有哪些算法,实际虚拟机使用得最多的是什么?
引用计数法和根可达性分析两种,用得最多是根可达性分析。
GC收集算法有哪些?他们的特点是什么?
复制、标记清除、标记整理。复制速度快,但是要浪费空间,不会内存碎片。标记清除空间利用率高,但是有内存碎片。标记整理算法没有内存碎片,但是要移动对象,性能较低。三种算法各有所长,各有所短。
JVM中一次完整的GC流程是怎样的?对象如何晋级到老年代?
对象优先在新生代区中分配,若没有足够空间,Minor GC;大对象(需要大量连续内存空间)直接进入老年态;长期存活的对象进入老年态。
如果对象在新生代出生并经过第一次MGC后仍然存活,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。
Java中的几种引用关系,他们的区别是什么?
强引用
一般的Object obj = new Object() ,就属于强引用。在任何情况下,只有有强引用关联(与根可达)还在,垃圾回收器就永远不会回收掉被引用的对象。
软引用SoftReference
一些有用但是并非必需,用软引用关联的对象,系统将要发生内存溢出(OuyOfMemory)之前,这些对象就会被回收(如果这次回收后还是没有足够的空间,才会抛出内存溢出)。
弱引用WeakReference
一些有用(程度比软引用更低)但是并非必需,用弱引用关联的对象,只能生存到下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。
虚引用PhantomReference
幽灵引用,最弱(随时会被回收掉)
垃圾回收的时候收到一个通知,就是为了监控垃圾回收器是否正常工作。
final、finally、finalize的区别?
在java中,final可以用来修饰类,方法和变量(成员变量或局部变量)
当用final修饰类的时,表明该类不能被其他类所继承。当我们需要让一个类永远不被继承,此时就可以用final修饰,但要注意:
final类中所有的成员方法都会隐式的定义为final方法。
使用final方法的原因主要有两个:
(1)把方法锁定,以防止继承类对其进行更改。
(2)效率,在早期的java版本中,会将final方法转为内嵌调用。但若方法过于庞大,可能在性能上不会有多大提升。因此在最近版本中,不需要final方法进行这些优化了。
final成员变量表示常量,只能被赋值一次,赋值后其值不再改变。
finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下
Object中的Finalize方法
即使通过可达性分析判断不可达的对象,也不是“非死不可”,它还会处于“缓刑”阶段,真正要宣告一个对象死亡,需要经过两次标记过程,一次是没有找到与GCRoots的引用链,它将被第一次标记。随后进行一次筛选(如果对象覆盖了finalize),我们可以在finalize中去拯救。
所以建议大家尽量不要使用finalize,因为这个方法太不可靠。在生产中你很难控制方法的执行或者对象的调用顺序,建议大家忘了finalize方法!因为在finalize方法能做的工作,java中有更好的,比如try-finally或者其他方式可以做得更好
String s = new String(“xxx”);创建了几个对象?
2个,
[if !supportLists]1、 [endif]在一开始字符串"xxx"会在加载类时,在常量池中创建一个字符串对象。
调用new时 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中字符串。
---------------------------------------------------------------------------------------------------------------------------------------------
摘要:
分代收集理论
1、绝大部分的对象都是朝生夕死。--区域:新生代
2、对象熬过多次垃圾回收,越来越难回收。--区域:老年代
3、新生代:98%死掉。
eden区;survvivor空间 from,to 8:1:1
4、堆空间大 6-8G G1;堆空间小 追求效率cms
-----------------------------------------------------------------------------------------------------------------------------------
内容:
----------------------------------------------------------------------------------------------------------------------
//这个地方不会在常量池中创建
private Stringcity;
private Stringregion;
public static void main(String[] args) {
//JVM首先会检查该对象是否在字符串常量池中,如果在,就返回该对象引用,否则新的字符串将在常量池中被创建。
//这种方式可以减少同一个值的字符串对象的重复创建,节约内存。
String str ="abc";
//首先在编译类文件时,"abc"常量字符串将会放入到常量结构中,在类加载时,“abc"将会在常量池中创建;
//其次,在调用 new 时,JVM 命令将会调用 String 的构造函数,同时引用常量池中的"abc” 字符串,
// 在堆内存中创建一个 String 对象;最后,str1 将引用 String 对象。
String str1 =new String("abc");
//这里就跟第一步类似。
Location location =new Location();
location.setCity("深圳");
location.setRegion("南山");
//首先会生成 ab 对象,再生成 abcd 对象,最后生成 abcdef 对象
String str2="ab" +"cd" +"ef";
//new Sting() 会在堆内存中创建一个a的String对象,
// “king"将会在常量池中创建
// 在调用intern方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回引用。
String a =new String("king").intern();
//调用 new Sting() 会在堆内存中创建一个b的String 对象,。
//在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回引用。
String b =new String("king").intern();
//所以 a 和 b 引用的是同一个对象。
if(a==b) {
System.out.print("a==b");
}else{
System.out.print("a!=b");
}
}