• 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、适用于读多写少的场景;

  • 相关阅读:
    2016年第9本:系统之美
    2016年第8本:不可思议的心理控制实验
    2016年第7本:非暴力沟通
    用SDWebImage渐变加载图片
    iOS相册、相机、通讯录权限获取
    屏蔽iOS10模拟器海量的垃圾debug信息
    Swift
    Swift
    PlaceholderImageView
    Swift
  • 原文地址:https://www.cnblogs.com/zhzJAVA11/p/10225250.html
Copyright © 2020-2023  润新知