JVM基础---面试用

、java的技术体系是什么?

java程序设计语言

各硬件平台上的java虚拟机

class文件格式

java API

第三方java类库

 

2、JDK8特性

什么是Lambda表达式?

什么是匿名内部类?

 

3、java内存管理是?

 

什么是程序计数器?

 

什么是java栈?

 

 什么是java堆?

 

什么是方法区?

 

 对象的创建在内存中的反应?

 

 

 什么是对象的结构?

 

哈希值是什么:

 

什么是对象的访问定位?

使用句柄(间接访问)

使用指针(直接访问)

 

什么是可达性分析法?

作为GCRoots的对象的引用才不会被垃圾回收;

虚拟机的内存结构?

 

内存分配的策略是什么?

 

什么是逃逸分析与栈上分配?

 

 

class文件是什么,它的文件结构由什么组成?

 

 什么是字节码指令?

 

什么是加载和存储指令?

 

 什么是运算指令?

 

 什么是类型转换指令?

 

 

什么是对象创建与访问指令?

 

什么是操作数栈管理指令?

 

什么是控制转移指令?

 什么是方法调用指令?

 

 什么是异常处理指令?

什么是同步指令?

 

 

什么是类加载机制?

 

类加载机制的时机?
类加载的整个过程:

 

1.初始化过程:

 

 不会被初始化的例子:

 

虚拟机引擎结构

运行时的栈帧结构是什么?

 

 栈帧的具体结构是什么?

 

操作数栈的具体流程?

public class myApplication{

public static int add(int a, int b){
return a+b;
}
}

 

 

 

 解释:运行前,a和b放在局部变量区,开始运行时,a先进栈,随后b进栈,a+b在栈中完成后弹栈,回到局部变量区中;

 

 

什么静态分配调用和动态分配调用?

静态分配调用-------多态------重载;

动态分配调用-------继承------重写;

 

最终的总结:

 

 

ArrayBlockingQueue(数组锁定队列)

 

是一个阻塞式的队列,继承自AbstractBlockingQueue,间接的实现了Queue接口和Collection接口;底层以数组的形式保存数据(看成一盒循环数组),

常用的操作包括add,offer,put,(插入操作)

remove,poll,take,peek;(取出操作)

1、add:内部实际上获取的offer方法,当Queue已经满了时,会抛出一个异常,不会阻塞;

2、offer:当Queue已经满了时,返回false,不会阻塞;

3、put:当Queue已经满了时,会进入等待,只要不被中断,就会插入数据到队列中,会阻塞,可以响应中断;

其中remove和add相互对应,也就是说,调用remove方法时,假如对列为空,则抛出异常;而poll和offer相互对应;

take和put相互对应,peek方法比较特殊,前3个取出方法,都会将元素从Queue的头部溢出,但peek不会,它是获取头部元素,peek方法也不会阻塞,当队列为空,直接返回NULL;

源码分析(版本1.8):

1、保存数据结构

1 /** The queued items */
2 final Object[] items;

2、全局锁

/** Main lock guarding all access */
final ReentrantLock lock;

这是一个掌管所有访问操作的锁,全局共享;

 

3、add和offer

 1 public boolean add(E e) {
 2     return super.add(e);
 3 }
 4 public boolean offer(E e) {
 5     checkNotNull(e);
 6     final ReentrantLock lock = this.lock;
 7     lock.lock();  // 一直等到获取锁
 8     try {
 9         if (count == items.length)  //假如当前容纳的元素个数已经等于数组长度,那么返回false
10             return false;
11         else {
12             enqueue(e);        // 将元素插入到队列中,返回true
13             return true;
14         }
15     } finally {
16         lock.unlock();        //释放锁
17     }
18 }

分析:一直等待获取锁----》当获取到锁之后,比较当前的元素个数与数组长度,当相等时,队列已经满了,无法插入,返回false---->否则进行入队操作,返回true;

4、put

 1 public void put(E e) throws InterruptedException {
 2     checkNotNull(e);
 3     final ReentrantLock lock = this.lock;
 4     lock.lockInterruptibly();  //可中断的获取锁
 5     try {
 6         while (count == items.length)    //当线程从等待中被唤醒时,会比较当前队列是否已经满了
 7             notFull.await();  //notFull = lock.newCondition 表示队列不满这种状况,假如现场在插入的时候
 8         enqueue(e);        //当前队列已经满了时,则需要等到这种情况的发生。
 9     } finally {            //可以看出这是一种阻塞式的插入方式
10         lock.unlock();
11     }
12 }

 

5、poll

 1 public E poll() {
 2     final ReentrantLock lock = this.lock;
 3     lock.lock();
 4     try {
 5         return (count == 0) ? null : dequeue();    //假如当前队列中的元素为空,返回null,否则返回出列的元素
 6     } finally {
 7         lock.unlock();
 8     }
 9 }

 

6、take

 1 public E take() throws InterruptedException {
 2     final ReentrantLock lock = this.lock;
 3     lock.lockInterruptibly();
 4     try {
 5         while (count == 0)    //线程在刚进入 和 被唤醒时,会查看当前队列是否为空
 6             notEmpty.await();    //notEmpty=lock.newCondition表示队列不为空的这种情况。假如一个线程进行take
 7         return dequeue();    //操作时,队列为空,则会一直等到到这种情况发生,返回出列元素;
 8     } finally {
 9         lock.unlock();
10     }
11 }

 

7、peek

 1 public E peek() {
 2     final ReentrantLock lock = this.lock;
 3     lock.lock();
 4     try {
 5         return itemAt(takeIndex); // null when queue is empty    
 6         }                       // 实际上 itemAt 方法就是 return (E) items[i];
 7                     // 也就是说 返回 数组中的第i个元素。
8  finally { lock.unlock(); }

 

8、remove

1 public E remove() {
2     E x = poll();
3     if (x != null)
4         return x;
5     else
6         throw new NoSuchElementException();
7 }

 

9、enqueue(入队)

 1 private void enqueue(E x) {        //因为调用enqueue的方法都已经同步过了,这里就不需要在同步了
 2     // assert lock.getHoldCount() == 1;
 3     // assert items[putIndex] == null;
 4     final Object[] items = this.items;
 5     items[putIndex] = x;        //putIndex是下一个放至元素的坐标
 6     if (++putIndex == items.length)    //putIndex+1, 并且比较是否与数组长度相同,是的话,则从数组开头
 7         putIndex = 0;            //插入元素,这就是循环数组的奥秘了
 8     count++;                //当前元素总量+1
 9     notEmpty.signal();            //给等到在数组非空的线程一个信号,唤醒他们。
10 }

 

10、dequeue(出队)

 1 private E dequeue() {
 2     // assert lock.getHoldCount() == 1;
 3     // assert items[takeIndex] != null;
 4     final Object[] items = this.items;
 5     @SuppressWarnings("unchecked")
 6     E x = (E) items[takeIndex];
 7     items[takeIndex] = null;        //将要取出的元素指向null 表示这个元素已经取出去了
 8     if (++takeIndex == items.length)    //takeIndex +1,同样的假如已经取到了数组的末尾,那么就要重新开始取
 9         takeIndex = 0;            //这就是循环数组
10     count--;                
11     if (itrs != null)        
12         itrs.elementDequeued();        //这里实现就比较麻烦,下次单独出一个吧,可以看看源码
13     notFull.signal();        //同样 需要给 等待数组不满这种情况的线程一个信号,唤醒他们。
14     return x;
15 }

 

为什么ArrayBlockingQueue是线程安全的?

当一个线程对已经满了的阻塞队列进行入队操作时会阻塞,除非有另一个线程进行了出队操作,当一个线程都

一个空的阻塞队列进行出队操作时也会阻塞,除非有另外一个线程进行了入队操作;

 

CopyOnWriteArrayList的原理

这是一个ArrayList的变体,其原理为初始化的时候只有一个容器,很常一段时间,这个容器数据、数量等没有发生变化的时候,线程都是读取同一个容器的数据,所以这样大家读到的数据都是唯一、一致、安全的,但后来有一个数据进入容器,CopyOnWriteArrayList 的底层实现是先copy出一个容器(副本容器),再往新的容器里添加这个数据,最后把新容器的引用地址赋给旧容器地址,但在添加这个数据的期间,其他线程去读取数据,仍然是读取到旧容器的数据;

 

 

 

原理分析:

 

 

上述4个构造方法都创建CopyOnWriteArrayList对象,并且会创建一个Object类型的数组,然后赋值给成员array;

接下来了解它的增、删、改、读方法是怎么处理的了:

 

 首先是加锁确保同一时间只有一个线程在添加元素,然后使用Arrays.copyOf() 方法复制出另一个新的数组,而且新的数据的长度比原来数组的长度+1,副本复制完毕,新添加的元素也赋值完毕,最后又把新的副本数组赋值给了旧的数组,最后在finally语句块中将锁释放;

 

(注:由于所有写操作都是在新数组进行的,这个时候如果有线程并发的写,则通过锁来控制,如果有线程

并发的读,则分几种情况:

1、如果写操作未完成,那么直接读取原数组的数据;

2、如果写操作完成,但是引用还未指向新数组,那么也是读取原数组数据;

3、如果写操作完成,并且引用已近指向新的数组,那么直接从新的数组中读取数据;

 

 

 

 

 接着来看remove,删除元素,就是判断要删除的元素是否是最后一个,如果最后一个直接在赋值副本数组的时候,复制长度为旧数组的length-1, 但是如果不是最后一个元素,就先复制旧的数组的index前面元素到新的数组中,然后在复制旧数组中index后面的元素到数组中,最后再把新数组复制给旧数组的引用;

 

CopyOnWriteArrayList的使用场景

1、由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组较多的情况下,可能导致young gc或 full gc

2、不能用于实时读的场景,因为调用一个set操作之后,读取到的数据还可能是旧的;

3、适用于读多写少的场景;

 

posted on 2019-01-05 17:04  章鱼哥儿  阅读(227)  评论(0编辑  收藏  举报