JVM:
Q:JVM内存是如何划分的?
https://www.cnblogs.com/dolphin0520/p/3613043.html
https://blog.csdn.net/zhangjianjaEE/article/details/78655783
Q:谈谈垃圾回收机制?为什么引用计数器判定对象是否回收不可行?知道哪些垃圾回收算法?
https://blog.csdn.net/zcw4237256/article/details/79042308
java中是通过引用来和对象进行关联的,也就是说如果要操作对象,必须通过引用来进行。那么很显然一个简单的办法就是通过引用计数来判断一个对象是否可以被回收。不失一般性,如果一个对象没有任何引用与之关联,则说明该对象基本不太可能在其他地方被使用到,那么这个对象就成为可被回收的对象了。这种方式成为引用计数法。
这种方式的特点是实现简单,而且效率较高,但是它无法解决循环引用的问题,因此在Java中并没有采用这种方式
Q:Java中引用有几种类型?在Android中常用于什么情景?
1)强引用,就是指在程序代码中普遍存在的,类似于Object obj=new Object()这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
2)软引用是用来描述一些还有用但并非必要的对象,对于软引用关联的对象,在系统将要发生溢出异常之前,将会把这些对象列进回收范围之中进行二次回收。如果这次回收还没有足够内存,才会抛出内存溢出异常。
3)弱引用也用来描述非必须对象的,但是它的强度比软引用更弱一点,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集工作时,无论当前内存是否足够,都会回收掉只被弱引用的对象**(Android多用于静态内部类+弱引用的方式创建Handler,防止内存泄漏)**
4)虚引用也成为幽灵引用或者幻影引用,它是最弱的一种引用关系,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置成虚引用关联的唯一目的就是能够在这个对象被收集器回收时收到一个系统通知。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
Q:类加载的全过程是怎样的?什么是双亲委派模型?
1、类加载过程:加载、验证、准备、解析、初始化
加载
在加载阶段,虚拟机主要完成三件事:
1.通过一个类的全限定名来获取定义此类的二进制字节流。
2.将这个字节流所代表的静态存储结构转化为方法区域的运行时数据结构。
3.在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区域数据的访问入口
验证
验证阶段作用是保证Class文件的字节流包含的信息符合JVM规范,不会给JVM造成危害。如果验证失败,就会抛出一个java.lang.VerifyError异常或其子类异常。验证过程分为四个阶段
1.文件格式验证:验证字节流文件是否符合Class文件格式的规范,并且能被当前虚拟机正确的处理。
2.元数据验证:是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言的规范。
3.字节码验证:主要是进行数据流和控制流的分析,保证被校验类的方法在运行时不会危害虚拟机。
4.符号引用验证:符号引用验证发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在解析阶段中发生。
准备
准备阶段为变量分配内存并设置类变量的初始化。在这个阶段分配的仅为类的变量(static修饰的变量),而不包括类的实例变量。对已非final的变量,JVM会将其设置成“零值”,而不是其赋值语句的值:
pirvatestaticintsize=12;
那么在这个阶段,size的值为0,而不是12。final修饰的类变量将会赋值成真实的值。
解析
解析过程是将常量池内的符号引用替换成直接引用。主要包括四种类型引用的解析。类或接口的解析、字段解析、方法解析、接口方法解析。
初始化
在准备阶段,类变量已经经过一次初始化了,在这个阶段,则是根据程序员通过程序制定的计划去初始化类的变量和其他资源。这些资源有static{}块,构造函数,父类的初始化等。
至于使用和卸载阶段阶段,这里不再过多说明,使用过程就是根据程序定义的行为执行,卸载由GC完成。
使用
新线程—程序计数器----jvm栈执行(对象引用)-----堆内存(直接引用)----方法区
卸载
GC垃圾回收
2、双亲委派机制、意义、方法
https://blog.csdn.net/sinat_34979383/article/details/58704897
双亲委派模型的工作过程是:如果一个类加载器收到了类加载请求,它首先不会自己去加载这个类,而是将这个类委托给父加载器去完成,每一层加载器都是如此,因此所有加载请求最终应该传送到顶层的启动类加载器中,只有父加载器反馈无法加载时,子加载器才会自己尝试去加载。
机制
启动(Bootstrap)类加载器----->标准扩展(Extension)类加载器—>系统(System)类加载器---->上下文(Custom)类加载器
从左到右加载:首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
意义
防止内存中出现多份同样的字节码
使用委托机制,会递归的向父类查找,如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器去加载类B,如果A加载器已加载类A,那么B使用A的类加载器进行加载时,就不会在加载类A的字节码了
方法
《1》启动(Bootstrap)类加载器
《2》标准扩展(Extension)类加载器
《3》应用程序类加载器(Application )
《4》上下文(Custom)类加载器
Q:工作内存和主内存的关系?在Java内存模型有哪些可以保证并发过程的原子性、可见性和有序性的措施?
https://blog.csdn.net/shuiming1991/article/details/73823845
所有线程共享主内存(主存);每个线程有自己的工作内存(寄存器)
保证措施:
https://blog.csdn.net/j2370530/article/details/55057137
https://www.cnblogs.com/jiliunyongjin/p/7286602.html
Q:JVM、Dalvik、ART的区别?
什么是Dalvik:
Dalvik是Google公司自己设计用于Android平台的Java虚拟机。它可以支持已转换为.dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik应用作为独立的Linux进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。
什么是ART:
ART代表AndroidRuntime,Dalvik是依靠一个Just-In-Time(JIT)编译器去解释字节码,运行时编译后的应用代码都需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。
ART则完全改变了这种做法,在应用安装的时候就预编译字节码到机器语言,这一机制叫Ahead-Of-Time(AOT)预编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。
1. Dalvik与JVM的区别
(1)Dalvik指令集是基于寄存器的架构,执行特有的文件格式——dex字节码(适合内存和处理器速度有限的系统)。而JVM是基于栈的。相对于基于栈的JVM而言,基于寄存器的Dalvik VM实现虽然牺牲了一些平台无关性,但是它在代码的执行效率上要更胜一筹。
(2)每一个Android的App是独立跑在一个VM中的。因此一个App crash只会影响到自身的VM,不会影响到其他。Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik应用作为一个独立的Linux进程执行。
2. Dalvik与ART的区别
(1)在Dalvik下,应用每次运行都需要通过即时编译器(JIT)将字节码转换为机器码,即每次都要编译加运行,这虽然会使安装过程比较快,但是会拖慢应用的运行效率。而在ART环境中,应用在第一次安装的时候,字节码就会预编译(AOT)成机器码,这样的话,虽然设备和应用的首次启动(安装慢了)会变慢,但是以后每次启动执行的时候,都可以直接运行,因此运行效率会提高。
(2)ART占用空间比Dalvik大(原生代码占用的存储空间更大,字节码变为机器码之后,可能会增加10%-20%),这也是著名的“空间换时间大法"。
(4)预编译也可以明显改善电池续航,因为应用程序每次运行时不用重复编译了,从而减少了CPU的使用频率,降低了能耗。
Q:Java中堆和栈的区别?
https://blog.csdn.net/u011546655/article/details/52170470
在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。
堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。
这也是 Java 比较占内存的原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针!
并发:
Q:开启一个线程的方法有哪些?销毁一个线程的方法呢?
创建线程的最简单的方法就是创建一个实现Runnable 接口的类。Runnable抽象了一个执行代码单元。你可以通过实现Runnable接口的方法创建每一个对象的线程。为实现Runnable 接口,一个类仅需实现一个run()的简单方法
创建线程的另一个途径是创建一个新类来扩展Thread类,然后创建该类的实例。当一个类继承Thread时,它必须重载run()方法
销毁线程:https://blog.csdn.net/qq_37465368/article/details/80869218
(1)设置退出标志,使线程正常退出,也就是当run()方法完成后线程终止
(2)使用interrupt()方法中断线程
(3)使用stop方法强行终止线程(不推荐使用,Thread.stop, Thread.suspend, Thread.resume 和
Runtime.runFinalizersOnExit 这些终止线程运行的方法已经被废弃,使用它们是极端不安全的!)
stop()方法太过于暴力,会强行把执行一半的线程终止。这样会就不会保证线程的资源正确释放,通常是没有给与线程完成资源释放工作的机会,因此会导致程序工作在不确定的状态下
Q:同步和非同步、阻塞和非阻塞的概念
当两个或两个以上的线程需要共享资源,它们需要某种方法来确定资源在某一刻仅被一个线程占用。达到此目的的过程叫做同步(synchronization)
阻塞是指在某个动作未执行完之前,线程一直处于挂起状态
Q:Thread的join()有什么作用?
如果所调用线程仍在运行,isAlive()方法返回true,如果不是则返回false。但isAlive()很少用到,等待线程结束的更常用的方法是调用join(),描述如下:
final void join( ) throws InterruptedException
该方法等待所调用线程结束。
Q:线程的有哪些状态?
运行,挂起,阻塞,终止
Q:什么是线程安全?保障线程安全有哪些手段?
多线程访问同一个资源时,得到的是正确的结果。
线程同步,用synchronized修饰,调用被synchronized关键字修饰的方法。当一个线程在一个同步方法内部,所有试图调用该方法(或其他同步方法)的同实例的其他线程必须等待**:ReentrantLock和synchronized的区别**
Q:synchronized和volatile的区别?
https://blog.csdn.net/suifeng3051/article/details/52611233
https://www.cnblogs.com/tf-Y/p/5266710.html
- volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
- volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
- volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
- volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
Q:synchronized同步代码块还有同步方法本质上锁住的是谁?为什么?
锁住的是对象。
https://blog.csdn.net/shenshibaoma/article/details/53009505
Q:sleep()和wait()的区别?
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备
sleep() 和 wait() 的区别就是 调用sleep方法的线程不会释放对象锁,而调用wait() 方法会释放对象锁
集合:
Q:Java集合框架中有哪些类?都有什么特点
https://blog.csdn.net/sdgihshdv/article/details/72566485
Q:集合、数组、泛型的关系,并比较
https://blog.csdn.net/wangjingna/article/details/38278429
集合可放任意类型的元素,会自动增大,取出时要做类型转换
泛型集合只能放定义类型的元素,会自动增大,取出时不用做类型转换
数组只能放定义类型的元素,不会自动增大,取出时不用做类型转换
集合里所有的元素都是Object,如果元素是值类型,会自动装箱。
泛型集合可以定义元素类型,相对于集合,泛型集合可以避免装箱拆箱,提高性能,同时程序具有更好的可读性。
数组本身可以认为是一种泛型集合结构体
Q:ArrayList和LinkList的区别?
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
ArrayList内部是使用可増长数组实现的,所以是用get和set方法是花费常数时间的,但是如果插入元素和删除元素,除非插入和删除的位置都在表末尾,否则代码开销会很大,因为里面需要数组的移动。
LinkedList是使用双链表实现的,所以get会非常消耗资源,除非位置离头部很近。但是插入和删除元素花费常数时间。
Q:ArrayList和Vector的区别?
Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。
当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。
Q:HashSet和TreeSet的区别?
https://blog.csdn.net/u012546203/article/details/62043423
HashSet有以下特点
不能保证元素的排列顺序,顺序有可能发生变化
不是同步的
集合元素可以是null,但只能放入一个null
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
Q:HashMap和Hashtable的区别?
https://blog.csdn.net//article/details/81193887
比较
HashMap
HashTable
存储结构
数组 + 链表/红黑树
数组 + 链表
扩容方式
oldCap * 2
oldCap * 2 + 1
K,V能否为null
key, value 均可以为 null
key, value 均不可以为 null
线程是否安全
线程不安全
线程安全
Q:HashMap在put、get元素的过程?体现了什么数据结构?
在JDK1.8之前,HashMap采用桶+链表实现,本质就是采用数组+单向链表组合型的数据结构。它之所以有相当 快的查询速度主要是因为它是通过计算散列码来决定存储的位置。HashMap通过key的hashCode来计算hash值,不同的hash值就存在数组中不同的位置,当多个元素的hash值相同时(所谓hash冲突),就采用链表将它们串联起来(链表解决冲突),放置在该hash值所对应的数组位置上。在JDK1.8中,HashMap的存储结构已经发生变化,它采用数组+链表+红黑树这种组合型数据结构。当hash值发生冲突时,会采用链表或者红黑树解决冲突。当同一hash值的结点数小于8时,则采用链表,否则,采用红黑树。
Q:如何解决Hash冲突?
https://blog.csdn.net//article/details/81192975
Q:如何保证HashMap线程安全?什么原理?
http://www.importnew.com/21396.html
HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的Entry链表形成环,一旦成环,Entry的next节点永远不为空,产生死循环.
虽然HashMap不是线程安全的,但是它的效率会比Hashtable要好很多。这样设计是合理的。在我们的日常使用当中,大部分时间是单线程操作的。HashMap把这部分操作解放出来了。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap。ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。
Q:HashMap是有序的吗?如何实现有序?
无序的:https://blog.csdn.net/zgengen/article/details/54588539
有序实现:https://blog.csdn.net/zhuhao717/article/details/47444763
HashMap 的一个功能缺点是它的无序性,被存入到 HashMap 中的元素,在遍历 HashMap 时,其输出是无序的。如果希望元素保持输入的顺序,可以使用 LinkedHashMap 替代。还可用TreeMap 则根据元素的 Key 进行排序。
Q:HashMap是如何扩容的?如何避免扩容?
HashMap的自动扩容机制
HashMap内部的Node数组默认的大小是16,假设有100万个元素,那么最好的情况下每个hash桶里都有62500个元素,这时get(),put(),remove()等方法效率都会降低。为了解决这个问题,HashMap提供了自动扩容机制,当元素个数达到数组大小_loadFactor后会扩大数组的大小,在默认情况下,数组大小为16,loadFactor为0.75,也就是说当HashMap中的元素超过16_0.75=12时,会把数组大小扩展为2*16=32,并且重新计算每个元素在新数组中的位置
Q:hashcode()的作用,与equal()有什么区别?
https://www.cnblogs.com/keyi/p/7119825.html
hashCode()方法和equal()方法的作用其实一样,在Java里都是用来对比两个对象是否相等一致,那么equal()既然已经能实现对比的功能了,为什么还要hashCode()呢?
因为重写的equal()里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高,那么hashCode()既然效率这么高为什么还要equal()呢?
因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,所以我们可以得出:
1.equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比是绝对可靠的。
2.hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的。
Q:CurrentHashMap原理?
https://blog.csdn.net/jjc120074203/article/details/78625433
https://blog.csdn.net/qq_33589510/article/details/79962152
Q:List、Set、Map的区别?