迅雷面经汇总 (2)

运行时常量池:运行时常量池是方法区的一部分。Class 文件中的常量池(编译器生成的各种字面量和符号引用)会在类加载后被放入这个区域。除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。这部分常量也会被放入运行时常量池。

直接内存:直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError 异常出现。避免在Java堆和Native堆中来回复制数据。

JVM类加载

类加载的过程主要分为三个部分:

加载:指的是把class字节码文件从各个来源通过类加载器装载入内存中。

链接

初始化:对类变量初始化,是执行类构造器的过程。

链接又可以细分为

验证:为了保证加载进来的字节流符合虚拟机规范,不会造成安全错误。

准备:为类变量(注意,不是实例变量)分配内存,并且赋予初值。

解析:将常量池内的符号引用替换为直接引用的过程。

JVM垃圾回收算法

垃圾回收算法包括:标记-清除算法,复制算法,标记-整理算法,分代收集算法。

标记—清除算法:

标记/清除算法,分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。

标记阶段:标记的过程其实就是前面介绍的可达性分析算法的过程,遍历所有的GC Roots对象,对从GC Roots对象可达的对象都打上一个标识,一般是在对象的header中,将其记录为可达对象;

清除阶段:清除的过程是对堆内存进行遍历,如果发现某个对象没有被标记为可达对象,则将其回收。

标记-清除

复制算法:

将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。

将内存分为一块较大的 Eden 空间和两块较小的 Survior 空间,每次使用 Eden 空间和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象一次性复制到另一块 Survivor 空间上,最后清理 Eden 和 使用过的那一块 Survivor。HotSpot 虚拟机的 Eden 和 Survivor 的大小比例默认为 8:1,保证了内存的利用率达到 90 %。如果每次回收有多于 10% 的对象存活,那么一块 Survivor 空间就不够用了,此时需要依赖于老年代进行分配担保,也就是借用老年代的空间。

复制

标记—整理算法:

标记—整理算法和标记—清除算法一样,但是标记—整理算法不是把存活对象复制到另一块内存,而是把存活对象往内存的一端移动,然后直接回收边界以外的内存,因此其不会产生内存碎片。标记—整理算法提高了内存的利用率,并且它适合在收集对象存活时间较长的老年代。

标记—整理

分代收集算法:

分代回收算法实际上是把复制算法和标记整理法的结合,并不是真正一个新的算法,一般分为:老年代和新生代,老年代就是很少垃圾需要进行回收的,新生代就是有很多的内存空间需要回收,所以不同代就采用不同的回收算法,以此来达到高效的回收算法。

新生代:由于新生代产生很多临时对象,大量对象需要进行回收,所以采用复制算法是最高效的。

老年代:回收的对象很少,都是经过几次标记后都不是可回收的状态转移到老年代的,所以仅有少量对象需要回收,故采用标记清除或者标记整理算法

三、数据结构与算法 堆和栈 手写二分查找 public boolean Find(int target, int[][] array) { if (array == null || array.length == 0 || (array.length == 1 && array[0].length == 0)) return false; for (int i = 0; i < array.length; i++) { int begin = 0; int end = array[0].length - 1; while (begin <= end) { int mid = (begin + end) / 2; if (target > array[i][mid]) { begin = mid + 1; } else if (target < array[i][mid]) { end = mid - 1; } else { return true; } } } return false; } 二分法查找次数,100个数最多查找多少次? 20G大小的数据,但是内存也就10G,如何对20G数据排序? 手写冒泡 /** * 冒泡排序 * * @param array */ private static void bubbleSort(int[] array) { if (array == null || array.length == 0 || array.length == 1) return; for (int i = 0; i < array.length - 1; i++) { for (int j = 0; j < array.length - 1 - i; j++) {//注意数组边界 if (array[j] > array[j + 1]) { int temp = array[j]; array[j] = array[j + 1]; array[j + 1] = temp; } } } } private static void bubbleSort(int[] array) { if (array == null || array.length == 0 || array.length == 1) return; boolean flag = true;//发生了交换就为true, 没发生就为false,第一次判断时必须标志位true int k = array.length - 1; int pos = 0;//pos变量用来标记循环里最后一次交换的位置 for (int i = 0; i < array.length - 1; i++) { flag = false;//每次开始排序前,都设置flag为未排序过 for (int j = 0; j < k; j++) {//注意数组边界 if (array[j] > array[j + 1]) { int temp = array[j]; array[j] = array[j + 1]; array[j + 1] = temp; flag = true;//表示交换过数据; pos = j; } } k = pos; //判断标志位是否为false,如果为false,说明后面的元素已经有序,就直接return if (flag == false) return; } } 写一个后序遍历 四、操作系统 死锁,如何避免死锁

如果一个进程集合里面的每个进程都在等待只能由这个集合中的其他一个进程(包括他自身)才能引发的事件,这种情况就是死锁。

避免死锁:

安全状态

单个资源的银行家算法

多个资源的银行家算法

线程与进程的区别

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwzzfx.html