文章目录
- Queue的remove方法和poll方法的区别
- JVM的新生代和老年代
- CMS垃圾回收器(Concurrent Mark Sweep)
- G1垃圾回收器(Garbage-First)
- 双亲委派机制(JDK1.2以后被提出)
- TreeMap和HashMap的区别
- 接口和抽象类的区别
Queue的remove方法和poll方法的区别
- queue的增加元素方法add和offer的区别在于,add方法在队列满的情况下将选择抛异常的方法来表示队列已经满了,而offer方法通过返回false表示队列已经满了;在有限队列的情况,使用offer方法优于add方法;
- remove方法和poll方法都是删除队列的头元素,remove方法在队列为空的情况下将抛异常,而poll方法将返回null;
- element和peek方法都是返回队列的头元素,但是不删除头元素,区别在与element方法在队列为空的情况下,将抛异常,而peek方法将返回null
JVM的新生代和老年代
解释jvm会什么会分为新生代和老年代
- java中对象的特点,大部分对象都是朝生夕死
- 绝大多数新创建的对象都放到新生代
- 老年代放入经过多轮回收还生存的对象或者大对象
这样新生代采用复制算法清理垃圾,老年代采用标记清理算法或者标记整理算法清理垃圾,提高垃圾回收效率。
-
新生代
Eden区域和Survivor区域,Survivor区域由FromSpace和ToSpace组成。新建的对象都是由新生代分配内存,Eden空间不足会把存货的对象转移到Survivor中,新生代带下可以由-Xmn控制,也可以通过调节SurvivorRatio控制Eden与Survivor的比例。
MinorGC的过程: MinorGC采用复制算法。首先,把Eden和SurvivorFrom区域中存活的对象复制到SurvivorTo区域(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区),同时把这些对象的年龄+1(如果SurvivorTo不够位置了就放到老年区);然后,清空Eden和SurvivorFrom中的对象;最后,SurvivorTo和SurvivorFrom互换,原SurvivorTo成为下一次GC时的SurvivorFrom区。 -
老年代
用于存放新生代中经过多次垃圾回收仍然存活的对象。
老年代的对象比较稳定,所以MajorGC不会频繁执行。在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。
MajorGC采用标记—清除算法: 首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。
当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。
CMS垃圾回收器(Concurrent Mark Sweep)
并发标记清除垃圾回收器(老年代),基于标记清除算法实现的。主要分一下四个步骤
- 初始标记(CMS Initial mark)STW(stop the world)
- 并发标记(CMS concurrent mark)
- 重新标记(CMS remark)STW(stop the world)
- 并发清除(CMS concurrent sweep)
初始标记只标记GC Roots能直接关联到的对象,速度很快,并发标记是对在初始标记到的对象继续向下可达性分析到的对象进行标记,重新标记就是修正在并发标记的同时产生的对象进行标记。
优点:低延迟,用户体验感好
缺点:1.对cpu要求高,并发执行 2.会有浮动垃圾产生(并发清楚阶段),需要设置阈值(1.5为 68%,可以调高,1.6为92%) 3.标记清楚算法会产生碎片空间,需要定期整理。
调优参数
参数 | 作用 |
---|---|
-XX:+UseConcMarkSweepGC | 开启CMS |
-XX:+CMSInitatingOccupancyFraction | 浮动垃圾阈值 |
-XX:+CMSCompactAtFullCollection | FullGC时进行整理(默认开启) |
-XX:+CMSFullGCsBeforCompaction | 进行几次不整整理的fullGC后进行整理(默认0,每次都整理) |
G1垃圾回收器(Garbage-First)
垃圾优先回收器,每一种垃圾回收器的研发都是为了更多的缩小Stop the world时间
G1是在java堆很大的背景下被提出的,如果按照原来的分代回收,无论进行minorGC或者majorGC都会进行进行全部扫描,这个问题在java堆很大的时候就会出现扫描时间很长的情况。
特点
- 并行与并发,G1能充分利用服务器的硬件资源CPU,来缩短stop the world时间
- 分代收集 然保留分代的概念
- 空间整合 G1从整体上看采用的是标记-整理算法,但是在局部(region间)采用的是复制算法
- 可预测的停顿 可以让用户明确指定在一个长度为M毫秒的时间内,花费在垃圾回收的时间不得超过N妙
G1会把java堆分为若干个小块region,每个region都可以为(Eden,Survivor,Old,Humongous)区,G1会自己维护每个region的垃圾回收经验值,会优先回收垃圾多的region。大致分为以下4个步骤
- 初始标记(Initial Marking)
- 并发标记(Concurrent Marking)
- 最终标记(Final Marking)
- 筛选回收(Live Data Counting and Evacution)
双亲委派机制(JDK1.2以后被提出)
类加载器的分类
从java虚拟机的角度来看,类加载器只有两类
- 启动类加载器(BootStrap ClassLoader)位于虚拟机自身,由C++实现
- 其他类加载器,由java实现,独立于虚拟机外部,全部继承于抽象类 java.long.classloader
从java开发人员的角度来看,可以分为四类
- 启动类加载器(BootStrap ClassLoader),这个类加载器负责将放在JAVA_HOME\lib目录中或者被-Xbootclasspath参数所指定的路径中的并且是虚拟机识别的(进按照名称识别)类加载到虚拟机内存中
- 扩展类加载器(Extension ClassLoader),负责加载JAVA_HOME\lib\ext目录中的或者被java.ext.dirs系统变量所指定的路径中的所有类库
- 应用程序类加载器(Application ClassLoader)负责加载用户类路径(ClassPath)上所指类库
- 自定义类加载器 (User ClassLoader)
双亲委派模型在JDK1.2期间被引入并被广泛应用,他不是一个强制的约束模型,而是java设计者推荐给开发者的一种类加载器实现方式。
双亲委派模型的工作过程: 如果一个类加载器接收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器,每个类加载器都是这么做的,因此每个类加载请求都应该传送到最顶层的启动类加载器(BootStrap Classloader)中,只有在父加载器无法完成类加载时,子加载器才会尝试自己去加载。
好处:
- 使用层级关系避免重复加载
- 保证java程序稳定运行,java核心api定义类型不会被随意替换
TreeMap和HashMap的区别
Hashmap的实现
1.7以前 数组+链表 头插法
1.8 数组+链表+红黑树 尾插法
当链表超过8并且数组长度小于64时会优先扩容,数组长度为64时,会将链表转为红黑树,但是在进行删除操作后,只有红黑树中的元素小于6后才会进行红黑树向链表转换,扩容因子为0.75,扩容是进行2倍扩容
分桶操作是先对key进行hash操作然后等到的数字于数组长度进行位运算,以得到桶下标
允许有null值和null键
TreeMap
不允许出现重复的key;
可以插入null键,null值;
可以对元素进行排序;
接口和抽象类的区别
Java接口和Java抽象类有太多相似的地方,又有太多特别的地方,究竟在什么地方,才是它们的最佳位置呢?把它们比较一下,你就可以发现了。
- Java接口和Java抽象类最大的一个区别,就在于Java抽象类可以提供某些方法的部分实现,而Java接口不可以(就是interface中只能定义方法,而不能有方法的实现,而在abstract class中则可以既有方法的具体实现,又有没有具体实现的抽象方法),这大概就是Java抽象类唯一的优点吧,但这个优点非常有用。如果向一个抽象类里加入一个新的具体方法时,那么它所有的子类都一下子都得到了这个新方法,而Java接口做不到这一点,如果向一个Java接口里加入一个 新方法,所有实现这个接口的类就无法成功通过编译了,因为你必须让每一个类都再实现这个方法才行,这显然是Java接口的缺点。这个在我的另外一篇博客mapreduce 新旧API 区别中有提到类似的问题,在新的mapreduce api中更倾向于使用抽象类,而不是接口,因为这更容易扩展。原因就是上面划线部分所说的。
- 一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的等级结构中,而由于Java语言的单继承性,所以抽象类作为类型定义工具的效能大打折扣。在这一点上,Java接口的优势就出来了,任何一个实现了一个Java接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个Java接口,从而这个类就有了多种类型。(使用抽象类,那么继承这个抽象类的子类类型就比较单一,因为子类只能单继承抽象类;而子类能够同时实现多个接口,因为类型就比较多。接口和抽象类都可以定义对象,但是只能用他们的具体实现类来进行实例化。)
- 从第2点不难看出,Java接口是定义混合类型的理想工具,混合类表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为。
- 结合1、2点中抽象类和Java接口的各自优势,具精典的设计模式就出来了:声明类型的工作仍然由Java接口承担,但是同时给出一个Java 抽象类,且实现了这个接口,而其他同属于这个抽象类型的具体类可以选择实现这个Java接口,也可以选择继承这个抽象类,也就是说在层次结构中,Java 接口在最上面,然后紧跟着抽象类,这下两个的最大优点都能发挥到极至了。这个模式就是“缺省适配模式”。在Java语言API中用了这种模式,而且全都遵循一定的命名规范:Abstract +接口名。(A extends AbstractB implements interfaceC,那么A即可以选择实现(@Override)接口interfaceC中的方法,也可以选择不实现;A即可以选择实现(@Override)抽象类AbstractB中的方法,也可以选择不实现)