JVM内存模型
链接
静态方法区:全局、静态变量。
方法栈:程序指令以及局部变量表。
堆:所有线程共享,存放对象实例。
静态方法区
静态方法区,又称为永久代(Perm Generation)。它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
堆
堆可以划分为新生代和老年代。
新生代又可以划分为一个Eden区和两个Survivor(幸存)区。
按照规定,新对象会首先分配在Eden中(如果对象过大,比如大数组,将会直接放到老年代)。在GC中,Eden中的对象会被移动到survivor中,直至对象满足一定的年纪(定义为熬过minor GC的次数),会被移动到老年代。
新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 。
默认的,Eden : from : to = 8 : 1 : 1 ,即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。
垃圾回收(GC)
垃圾回收的意义
垃圾回收的出现解放了C++中手工对内存进行管理的大量繁杂工作,手工malloc,free不仅增加程序复杂度,还增加了bug数量。
分代收集。即在新生代和老生代使用不同的收集方式。
在垃圾收集上,目标主要有:
加大系统吞吐量(减少总垃圾收集的资源消耗);
减少最大STW(Stop-The-World)时间;
减少总STW时间。
可达性检测
引用计数
其主要思想就是维护一个counter,当counter为0的时候认为对象没有引用,可以被回收。缺点是无法处理循环引用。在当前的JVM中应该是没有被使用的。
根搜算法
思想是从gc root根据引用关系来遍历整个堆并作标记,称之为mark,等会在具体收集器中介绍并行标记和单线程标记。之后回收掉未被mark的对象,好处是解决了循环依赖这种『孤岛效应』。这里的gc root主要指:
a.虚拟机栈(栈桢中的本地变量表)中的引用的对象
b.方法区中的类静态属性引用的对象
c.方法区中的常量引用的对象
d.本地方法栈中JNI的引用的对象
回收策略
复制
主要用在新生代的回收上,通过from区和to区的来回拷贝。需要特定的结构(也就是Young区现在的结构)来支持,对于新生成的对象来说,频繁的去复制可以最快地找到那些不用的对象并回收掉空间。所以说在JVM里YGC一定承担了最大量的垃圾清除任务。
标记清除/标记整理
主要用在老年代回收上,通过根搜的标记然后清除或者整理掉不需要的对象。
进入老年代的条件
- 存活对象过多。在s1和s2都已经溢出了。如果从eden迁往survior区时,发现放不下,则直接进入 old Gen
- 从eden到s区来回拷贝次数达到一定的数量,总没有回收掉,进入old区。(从eden到survior1迁到,引用持有中,s1中放不下新迁对象,则清理s1,存活对象,晋升入s2;再下次或继续迁移,就把s2中的。准备说,可能是,这些个对象从s1<->s2来回拷贝一定次数后,会进入old Gen)。这块Servivor Space 调整合适的存活次数 Threshold 通过-XX:MaxTenuringThreshold。但也只是一个建议,最终仍由虚拟机决定
复制和标记清除/整理的区别
为什么新生代要用复制?因为对新生代来讲,一次垃圾收集要回收掉绝大部分对象,我们通过冗余空间的办法来加速整理过程(不冗余空间的整理操作要做swap,而冗余只需要做move)。同时可以记录下每个对象的『年龄』从而优化『晋升』操作使得中年对象不被错误放到老年代。
而反过来老年代偏稳定,我们哪怕是用清除,也不会产生太多的碎片,并且整理的代价也并不会太大。
Java常见面试题和答案
HashMap和Hashtable的区别
都实现了Map接口。
HashMap 类没有分类或者排序,它允许一个 null 键和多个 null 值,而且是不是线程安全的。
Hashtable 类似于 HashMap,但是不允许 null 键和 null 值。
最大的区别是:HashMap不是线程安全的,而Hashtable是同步/线程安全的。