java 什么时候复制 静态变量 java的对象的默认复制机制_java 对象复制


java虚拟机由4个部分组成,

  • Class Loader类加载器,加载class文件到内存中
  • Execution Engine解析class文件的字节码指令去执行
  • Natvie Inerface 融合不同开发语言的原生库为java所用
  • Runtime Date Area JVM内存空间结构模型

为什么使用反射?

java反射机制是在运行状态中,对于任意一个类,都是够知道这个类的属性和方法,对于任意一个类都能够调用它任意的方法和属性,这种动态获取信息的以及动态调用对象方法的功能称之为java反射。

类从编译到执行的过程

编译器将Robot.java源文件编译成Robot.class字节码文件

ClassLoader将字节码文件转换成Class<Robot>对象

什么是ClassLoder?

ClassLoder在java中有这非常重要的作用,它主要工作在class装载的加载阶段,其主要作用是从系统外部获取Class二进制数据流,它是java的核心组件。所有Class都是由ClassLoader进行加载的,ClassLoader复制将Class文件里的二进制数据流装载进系统然后交给Java虚拟机进行连接,初始化等操作。

ClasLoder的种类

  • BootStrapClassLoder :C++编写 ,加载核心类库 java.*
  • ExtClassLoader: java编写加载扩展库 javax.*
  • AppClassLoader : java编写,加载程序所在目录
  • 自定义ClassLoader: java编写定制化加载

ClassLoader的双亲委派机制

如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。

类的加载方式

  • 隐式加载:new
  • 显式加载:loadClass,forName,

Class.forName得到的class是已经初始化完成的(也就是初始化了类静态变量的值以及代码块代码)

ClassLoader.loadClass得到的是class还没有链接的(只是加载到内存中)

java的内存模型

线程私有: 程序计数器、虚拟机栈,本地方法栈。

线程共享的:mateSpace、java堆

  • 程序计数器 1当前线程所执行的字节码行号指示器,2改变计数器的值来选取下一条需要执行的字节码指令,3和线程是一对一的关系即“线程私有”,4java方法计数如果是native方法则计数器值为undefined,5不会发生内存泄漏。
  • java虚拟机栈 1java方法执行的内存模型,2包含多个栈帧。
  • 本地方法栈 这部分主要与虚拟机用到的 Native 方法相关,一般情况下, Java 应用程序员并不需要关心这部分的内容。
  • java堆是 JVM 所有线程共享的部分,在虚拟机启动的时候就已经创建。所有的对象和数组都在堆上进行分配。这部分空间可通过 GC 进行回收。
  • mateSpace 元空间。

JVM三大性能调优参数-Xms、-Xmx、-Xss的含义

-Xss:规定了每个线程虚拟机栈(堆栈)的大小。258k足够,影响并发线程数的大小。

-Xms:初始的java堆的大小,改线程刚创建出来的java堆的大小。如果超过到了java堆容量就会扩容,扩容至-Xmx大小。

-Xmx:java堆能扩容至最大大小。(一般Xms和Xmx设置一样,应该扩容时发生内存抖动影响性能)

java内存的堆和栈的区别

-内存分配策略

  • 静态存储;在编译时确定每个数据目标在运行时的存储空间需求。
  • 栈式存储:动态存储,数据区需求在编译时未知,运行时模块入口前确定。
  • 堆式存储;编译时或运行时模块入口都无法确定,动态分配。
  • 管理方式;栈自动释放,堆需要GC处理。
  • 空间大小;栈比堆小,
  • 碎片相关;栈产生的碎片远小于堆。
  • 分配方式;栈支持静态分配和动态分配,而堆仅仅支持动态分配。
  • 效率;栈的效率比堆高

不同JDK版本之间的intern()的区别---JDK6 VS JDK7+


String s = new String("a");
s.intern();


  • jdk6 ; 当调用intern方法时,如果字符串常量池先前已创建出改字符串对象。则返回池中该字符串的引用,否则将此字符串对象添加到字符常量池中,并且返回改字符串对象的引用。
  • jdk7+; 当调用intern方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中改字符串的引用,否则将该字符串对象已经已经在与java堆中,则将堆中对此对象的引用添加到字符串常量池中,并且返回该引用;如果堆中不存在,则在池中创建该字符串并返回其引用。

GC垃圾回收机制

什么对象被判定为垃圾的标准?

当一个对象没有被任何对象引用时,对系统而言它就是垃圾,占据的内存就应该被释放,同时此对象应该被销毁。

判断对象有没有被引用有两种方法:

  • 引用计数算法,通过判断对象的引用数量来决定对象是否可以被回收,每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1。优点:直接判断引用计数器为0的对象,执行效率高,程序执行受影响较小。确定是无法检测出循环引用的情况,导致内存泄漏。
  • 可达性分析算法,通过判断对象的引用链是否可达,离散数学图论,程序把所有的引用关系看做一张图,从GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路劲,就被成为引用链,Reference Chain,当一个对象从GC Roots没有Reference Chain和引用链相连,从图论上来说就是从GC Roots到这个对象是不可达。这样就被认为这个对象是不可用,这样这个对象就被标记为垃圾了。
    可用做为GC Root的对象,a.虚拟机栈中引用的对象(栈帧中的本地变量表)。b.方法区中的常量引用的对象。 c.方法区中的类静态属性引用的对象。d.本地方法栈中JNI(Native方法)的引用对象。e.活跃线程的引用对象。

谈谈你了解的垃圾回收算法

标记清除算法

  • 标记:从根集合进行扫描对存活的对象进行标记。
  • 清除:对堆内存从头到尾进行线性遍历,回收不可达对象内存。

标记清楚算法由于不需要移动原本的对象位置,直接抽取清除垃圾对象,比较高效,但是内存碎片化比较严重。

复制算法

分为对象面和空闲面,对象在对象面上创建,存活的对象被从对象面复制到空闲面,将对象面所有的对象内存清除。

标记整理算法

  • 标记:从根集合进行扫描对存活的对象进行标记。
  • 清除:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。

分代收集算法

垃圾回收算法的组合拳,按照对象生命周期的不同划分以采用不同的垃圾回收算法,目的提高JVM的回收效率。(java6,7堆内存分为年轻代老年代永久代这三个模块,jdk8版本永久代被去除,年轻代对象存活率低采用复制算法,老年代对象存活率高采用标记清楚算法或者标记整理算法)

分代收集算法的GC分为两种

Minor GC ;是在年轻代中工作,采用的是复式算法...

常见的垃圾收集器

年轻代常见的垃圾收集器

Serial收集器(-XX:+UseSerialGC,复制算法)

  • 单线程收集,进行垃圾收集时,必须暂停所有的工作线程
  • 简单高效,Client模式下默认的年轻代收集器

ParNew收集器(-XX:+UseParNewGC,复制算法)

  • 多线程收集,其余的行为,特点和Serial收集一样
  • 单核执行效率不如Serial,在多核执行才有优势

Pa(Ru):包含Running等待线程调度选中和Ready在线程池中等待线程调度和选中

  • 比起关注用户线程停顿时间,更关注系统的吞吐量。(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))
  • 在多核执行才有优势,Server模式下默认年轻代收集器

老年代常见的垃圾收集器

Serial Old收集器(-XX:+UseSerialOldGC,复制算法)

  • 单线程收集,进行垃圾收集时,必须暂停所有工作线程
  • 简单高效,Client模式下默认的老年代收集器

Parallel Old收集器(-XX:+UseParallelOldGC,标记-整理算法)

  • 多线程,吞吐量优先
  1. CMS收集器(-XX:+UseConcMarkSweepGC,标记-清除算法)
  2. G1收集器(-XX:+UseG1GC,复制+标记-整理算法)
  3. Garbage First 收集器的特点
  • 并发和并行
  • 分带收集
  • 空间整合
  • 可预测的停顿
  • 将整个Java堆内存划分多个相等的region
  • 年轻代和老年代不在物理隔离

GC相关的面试题QC

Object的finalize()方法的作用是否与C++的析函数作用相同?

  • 与C++的析构函数不同,析构函数调用确定,而它的是不确定的
  • 将未被引用的对象放置于F-Queue队列
  • 方法执行随时可能被终止
  • 给予对象最后一次重生的机会

Java中的强引用,软引用,弱引用,虚引用有什么用。

强引用(Strong Reference)

  • 最普遍的引用:Object obj = new Object()
  • 抛出OutOfMemoryError终止程序也不回被回收具有强引用的对象
  • 通过将对象设置为null来弱化引用,使其被回收

软引用(Soft Reference)

  • 对象处在有用但是非必须状态
  • 只有当内存空间不足时,GC会回收改引用的对象的内存
  • 可以用来实现内存敏感的高速缓存
String str  = new String("abc");
SoftReference<String> softRef = new SoftReference<String>(str);


弱引用(Weak Reference)

  • GC时被回收
  • 被回收的概率也不大,因为GC线程优先级比较低
  • 适用于引用偶尔被使用且不影响垃圾收集的对象
String str  = new String("abc");
WeakReference<String> softRef = new WeakReference<String>(str);


虚引用(PhantomReference)

  • 不会觉得对象的生命周期
  • 任何时候都可能被垃圾回收器回收
  • 跟踪对象被垃圾回收器回收的活动,起哨兵作用
  • 必须和引用队列ReferenceQueue联合使用
String str  = new String("abc");
ReferenceQueue<String> queue = new ReferenceQueue<String>(str);
PhantomReference ref = new PhantomReference(str ,queue)


java 什么时候复制 静态变量 java的对象的默认复制机制_java 析构函数_02


引用队列(ReferenceQueue)

  • 无实际存储结构;存储逻辑依赖于内部节点之间的关系来表达
  • 存储关联的且被GC的软引用,弱引用以及虚引用

关于JDK版本的选择

最好选择JDK8,JDK11,因为这是Oracle长期支持的版本。

什么是进程和什么是线程

进程和线程的由来

  1. 串行:初期的计算机智能串行执行任务,并且需要长时间等待用户的输入
  2. 批处理:预先将用户的指令集中成清单,批量串行处理用户的指令,仍然无法并发执行
  3. 进程:进程独占内存空间,保存各自运行的状态,互相间不干扰且可以相互切换,为并发处理任务提供了可能。
  4. 线程:共享进程的内存资源,相互间切换更加快速,支持更细粒度的任务控制,使进程内的子任务得以并发执行。
  • 线程不能看做独立应用,而进程可看做独立的应用
  • 进程有独立的地址空间,互相不影响,线程只是进程的不同执行路径
  • 线程没有独立的地址空间,多进程的程序比多线程的程序健壮
  • 进程的切换比线程的切换开销大

Java进程和线程的关系?

  • Java对操作系统提供你的功能进行封装,包括进程的线程
  • 运行一个程序会产生一个进程,进程包装至少一个线程
  • 每个进程对应一个JVM实例,多个线程共享JVM里的堆
  • Java采用单线程编程模型,程序会自动创建主线程
  • 主线程可以创建子线程,原则上要后于子线程完成执行

Thread中的start和run方法的区别?

  • 调用start()方法会创建一个新的子线程并启动
  • run()方法是是Thread的一个普通方法的调用

Thread和Runnable是什么关系?

  • Thread是实现了Runnable的类,使得run支持多线程
  • 因类的单一继承原则,推荐多使用Runnable接口

如何给run()方法传参?

  • 构造函数传参
  • 成员变量传参
  • 回调函数传参

如何实现处理线程的返回值

  • 主线程等待法
  • 使用Thread类的join()阻塞当前以等待子线程处理完毕
  • 通过Callable接口实现:通过FutureTask 或者线程池获取

线程的六个状态

  • 新建(New):创建后尚未启动的线程的状态,(新创建了一个线程对象,还没调用start方法)
  • 运行(Runneble):包含Running等待线程调度选中和Ready在线程池中等待线程调度和选中
  • 无限期等待(Waiting):不会被分配CPU执行时间,需要显式被唤醒
  1. 没有设置Timeout参数的object.wait()方法。
  2. 没有设置Timeout参数的Thread.join()。
  3. LockSupport.part()方法。
  • 限期等待(Timed Waiting):在一定时间后会由系统自动唤醒
  1. Thread.sleep()方法。
  2. 设置了Timeout参数的object.wait()方法。
  3. 设置了Timeout参数的Thread.join()。
  4. LockSupport.parkNanos()方法。
  5. LockSupport.parkUntil()方法。
  • 阻塞(Blocked):等待获取排他锁
  • 结束(Terminated):已终止线程的状态,线程已经结束执行

sleep和wait的区别

基本差别

  • sleep是Thread类的方法,wait()是object类中定义的方法
  • sleep方法可以在任何地方使用,wait()方法只能在synchronize方法或者synchronized块中使用

最主要的本质区别

  • Thread.sleep()只会让出CPU,不会导致锁行为的改变
  • Object.wait()不仅让出CPU,还会释放占有的同步资源的锁

notify和notifyAll的区别

两个概念

  • 锁池EntryList,当其他线程执行到有锁方法时,无法获取锁只能阻塞时,当前线程进入锁池
  • 等待池WaitSet,当使用wait方法释放锁,进入等待池,等待其他线程唤醒或者等待期限结束。

notifyAll会让所有处于等待的线程全部进入锁池去竞争获取锁的机会,notify只会随机选取一个处于等待池的线程进入锁池去竞争获取锁的机会

--个人理解当所有线程需要锁才能执行下,notify只会随机唤醒一个线程去抢锁执行,其他线程没有被唤醒根本不会去抢锁。notifyAll会唤醒所有的线程去抢锁,抢到锁的执行,没有抢到锁的在锁池,一旦抢到锁依然会执行。

yield的概念

当调用Thread.yield()函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能忽视这个暗示。(就算出让CPU也不会释放锁)

如何中断线程

调用interrupt(),通知线程应该被中断了

  1. 如果线程处于被阻塞状态,那么线程将立即退出被阻塞状态,并抛出一个InerruptException异常
  2. 如果线程处于正常活动状态,那么线程将改线程的中断标识设置true,被设置中断标志的线程将继续正常运行不受影响。

需要被调用的线程配合中断

  1. 在正常运行的任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程
  2. 如果线程处于正常的活动状态,那么会将该线程的中断标识设置为true,被设置中断标志的线程将继续正常运行,不受影响。

synchronized

互斥锁的特效

  • 互斥性:及在同一时间只允许一个线程持有某个对象的对象锁,通过这个特效来实现多线协调机制,这样在同一时间只有一个线程对需要同步的代码块(复合操作)进行访问,互斥性也称之为操作的原子性。
  • 可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于最后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能在本地缓存的某个副本上继续操作,从而引起不一致。

根据获取锁的分类:获取对象锁和获取类锁

获取对象锁

  1. 同步代码块(synchronized(this),synchronized(类的实例),锁的是小括号()中的实例对象)。
  2. 同步非静态方法,synchronized method,锁是当前对象的实例对象。

获取类锁

  1. 同步代码块(synchronized(类.class)锁的是小括号()中的Class对象)。
  2. 同步非静态方法,synchronized static method,锁是当前对象的类对象,Class对象。

对象锁和类锁的总结

  1. 有线程访问对象的同步代码块时,另外的线程可以访问该对象的非同步代码块;
  2. 若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象的同步代码块的线程会被阻塞;
  3. 若锁住的是同一个对象,一个线程在访问对象的同步方法时,另一个访问对象的同步方法的线程会被阻塞;
  4. 若锁住的是同一个对象,一个线程在访问对象的同步代码块时,另一个访问对象的同步方法的线程会被阻塞;反之亦然;
  5. 同一个类的不同对象的对象锁互不干扰;
  6. 类锁由于也是一直特殊的对象锁,因此和上诉1,2,3,4一致,而由于一个类只有一把对象锁,所有同一个类的不同对象使用类锁,将会是同步的;
  7. 类锁和对象锁互不干扰;

synchronized底层实现原理

这个概念性的东西太多了,还有JVM指令,容我先缓缓神...o(╥﹏╥)o