java基础面试

Java对象内存布局

对象头 :

对象自身运行时数据(MarkWorld) :哈希码GC分代年龄锁状态标志偏向线程ID偏向时间戳

class对象指针:

对象数组:

实例数据:对象实际数据

对齐填充

java基础面试_第1张图片

java基础面试_第2张图片

JVM内存结构

程序计数器:用于指示当前线程正在执行的字节码指令的地址。(线程私有)

Java虚拟机栈:用于存储局部变量、方法参数、部分计算结果和方法调用等信息。(线程私有)

本地方法栈:用于执行本地方法(Native Metho d)native:在java中有用native修饰的,表示这个方法不是java原生的(线程私有)

:用于存储Java对象实例和数组。堆是Java虚拟机管理的最大的一块内存区域。(线程共享)

元数据区:class结构信息,方法的定义,静态变量等.而常量池放到元数据区(线程共享)

直接内存:不是Java虚拟机运行时数据区的一部分,但也被频繁使用。它是通过Native函数库直接分配的内存,用于提高访问性能。

注意:

堆和元数据区是线程共享的,在Java虚拟机中只有一个堆、一个元数据区,并在JVM启动的时候就创建,JVM停止才销毁。

Java虚拟机栈、本地方法栈、程序计数器是每个线程私有的,随着线程的创建而创建,随着线程的结束而死亡。

java基础面试_第3张图片

在JVM中什么是直接内存

直接内存(Direct Memory)是指由操作系统直接管理的内存区域,而不是由Java堆来管理。它是通过使用Java NIO(New Input/Output)库中的ByteBuffer类来分配和操作的。

直接内存的分配和释放不受Java堆大小的限制,因此可以更灵活地使用大量的内存。它的分配是通过调用 ByteBuffer.allocateDirect() 方法来完成的,而不是使用传统的Java堆分配方式。

直接内存的优势在于它可以直接与操作系统交互,实现零拷贝(Zero-copy)操作提高了IO操作的效率。然而,由于直接内存的分配和释放需要与操作系统进行交互,所以其性能开销相对较高。

需要注意的是,直接内存的使用需要谨慎,过度使用直接内存可能导致操作系统的内存资源耗尽,从而影响系统的稳定性。因此,在使用直接内存时应该合理控制其分配的大小,并及时释放不再使用的内存。

java基础面试_第4张图片

JDK1.8堆内存结构

1. 新生代(Young Generation):新生代是Java堆中的一块区域,用于存放新创建的对象。它又被分为Eden区和两个Survivor区(S0和S1)。当Eden区满时,会触发Minor GC(Young GC),将存活的对象复制到Survivor区,同时清理掉Eden区和From区中的无用对象。

2. 老年代(Old Generation):老年代是Java堆中的另一块区域,用于存放长时间存活的对象。当Survivor区无法容纳对象时,会将对象放入老年代。当老年代满时,会触发Full GC,进行垃圾回收。MajorGC(Full GC)

java基础面试_第5张图片

元数据区:在JDK1.8中,永久代被移除,取而代之的是元数据区(Metaspace),用于存放类的元数据信息。元数据区的大小可以通过设置JVM参数来控制。

在JDK1.8中,Java堆内存结构的大小和分配方式可以通过JVM参数来进行调整,以满足不同应用程序的需求。

新生代与老年代的区别与关系

区别:

1. 存放对象类型:新生代主要用于存放新创建的对象,而老年代主要用于存放长时间存活的对象。

2. 垃圾回收频率:由于大部分对象的生命周期很短,新生代的垃圾回收频率通常比老年代高。新生代使用的垃圾回收算法通常是基于复制(Copying)的,这种算法适用于短生命周期的对象。

3. 垃圾回收时间:新生代的垃圾回收时间通常较短,因为只需要清理较小的一部分堆内存。而老年代的垃圾回收时间较长,因为需要遍历整个堆内存。

关系:

1. 年龄判定:新生代中的对象的年龄会逐渐增加,当达到一定年龄阈值时,会被晋升到老年代。

2. 内存分配:新生代中的内存空间被划分为Eden区和两个Survivor区(From和To)。新创建的对象首先被分配到Eden区,当Eden区满时,会触发Minor GC,将存活的对象复制到Survivor区,同时清理掉Eden区和From区中的无用对象。

3. 大对象直接进入老年代:如果对象的大小超过了新生代的一半大小,或者在Eden区无法分配连续内存空间时,该对象将直接被分配到老年代。

GC垃圾回收如何发现垃圾

1、引用计数算法:核心思想是,堆中的对象每被引用一次,则计数器加1,每减少一个引用就减1,当对象的引用计数器为0时可以被当作垃圾收集。(无法解决循环引用问题)

2、根搜索算法(可达性算法):垃圾回收器通过从根对象出发,沿着对象之间的引用链进行遍历,标记所有可达的对象。可达的对象意味着它们被程序中的其他对象引用着,因此是活动的,不是垃圾;剩余的节点则被认为是没有被引用到的节点,即可以当作垃圾。

Java中可作为GCRoot的对象有:

1.虚拟机栈中引用的对象

2.本地方法栈引用的对象

3.元空间中静态属性引用的对象

4.元空间中常量引用的对象

GC垃圾回收算法

1、标记-清除算法(markandsweep)

分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成之后统一回收掉所有被标记的对象 缺点:首先,效率问题,标记和清除效率都不高。其次,标记清除之后会产生大量的不连续的内存碎片

2、标记-整理算法

对标记-清除算法的改进。标记阶段是相同的,但标记完成之后不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,在移动过程中清理掉可回收的。缺点:耗时耗力

优点:不会产生大量不连续的内存碎片。

3、复制算法(copying)

该算法将堆内存分为两个相等大小的区域,每次只使用其中一个。当需要进行垃圾回收时,将存活的对象复制到另一个区域,并将原区域中的对象全部清除。复制算法简单高效,但会浪费一半的内存空间。

4、分代收集算法(generation)

当前主流JVM都采用分代收集(GenerationalCollection)算法,这种算法会根据对象存活周期的不同将内存划分为年轻代老年代,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。

新生代的比例默认为:8:1:1 Eden:S0:S1=8:1:1

java基础面试_第6张图片

注意:当一个大对象不足于存放到eden区时,就将存活对象直接存放到老年代。若是老年代也满了就会触发一次FullGC,也就是新生代、老年代都进行回收。

JVM调优

jvm调优可以对jvm的一些参数进行调整

1、增加堆内存 -Xms -Xma

2、调整垃圾回收机制,选择合适的垃圾回收算法

3、设置合适的年轻代和老年代的比例

-Xms8g:设置JVM最小内存为8g。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。(最小内存等于最大内存,减少GC回收次数)

-Xmx8g:设置JVM最大可用内存为8g。

-Xmn4g:设置年轻代大小为4G。

-XX:NewRatio=2 设置年轻代(包括Eden和两个Survivor区)与年老代的比值。设置为2,则年轻代与年老代所占比值为1:2,年轻代占整个堆栈的1/3。

-XX:MaxMetaSpaceSize=256m: 设置元空间大小为256m(避免发生OOM,内存溢出)

-XX:MaxTenuringThreshold=15:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入老年代。 对于老年代比较多的应用,可以提高效率。最大不操过15。

注意:

因为每个jdk的版本不一致,但是-X是标配,不会改变,-XX可能会因为版本不一致而发生改变

Java引用类型分类

java的引用类型一般分为四种:强引用软引用弱引用虚引用

强引用:普通的变量引用

软引用: 将对象用SoftReference软引用类型的对象包裹,正常情况不会被回收,但是GC做完后发现释放不出空间存放新的对象,则会把这些软引用的对象回收掉。(星巴克)

弱引用:将对象用WeakReference弱引用类型的对象包裹,弱引用跟没引用差不多,GC会直接回收掉,只要GC执行了,他就会被回收掉(沙县小吃)

虚引用:虚引用也**称为幽灵引用或者幻影引用,它是最弱的一种引用关系,几乎不用。

观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式,也叫发布-订阅模式,它定义了对象之间一对多的依赖关系,使得当一个对象状态改变时,所有依赖它的对象都能收到通知并自动更新。

观察者模式中,有两种类型的对象:观察者主题(或称为被观察者)。观察者将自己注册到主题,以便在主题的状态改变时接收通知。主题会维护一组观察者,并在自身状态改变时通知它们。主题通过调用观察者的统一接口来通知它们状态的变化。

springBoot事件监听就是观察者模式

你可能感兴趣的:(Java基础面试题,java,面试,开发语言)