【校招VIP】海信软开电话面经

09月15日 收藏 0 评论 0 java开发

【校招VIP】海信软开电话面经

转载声明:文章来源:https://www.nowcoder.com/discuss/531774228626997248

突击电话面经,没有事先通知

1,自我介绍

2,问项目,项目中遇到的问题与解决方案

3,java的内存机制,堆内存、栈内存
java的内存机制是指Java程序在运行时,如何管理和分配内存资源,Java采用自动内存管理机制,即通过垃圾回收器来自动管理内存的分配和释放。java的内存可以分为:
    方法区:用于存储类的结构信息,如类的成员变量,方法代码等
    栈内存:每个线程都有一个独立的栈,用于存储局部变量、方法参数、调用栈等。栈中的数据是按照先进后出的方式进行管理。
    堆内存:用于存储对象实例。所有通过new关键字创建的对象都在堆中分配内存。堆是java中最大的一块内存区域,它被所有线程共享。
    本地方法栈:用于存储java以外的本地方法的调用和执行
    程序计数器:用于记录当前线程执行的位置,也就是下一条要执行的指令。
    运行时常量池:用于存储编译期生成的各种字面量和符号引用。

垃圾回收器会自动监测并回收不再使用的内存对象,释放内存资源,当一个对象没有被任何引用所指向时,就会被判定为垃圾对象,垃圾回收期会将其回收并释放内存。

Java的内存机制使得程序员无需手动管理内存资源,大大简化了开发过程,提高了代码的可靠性和安全性,同时,合理地使用内存和优化内存使用也有助于提升程序的性能。

   栈内存:指的是每个线程在运行时独立拥有的一块内存空间,用于存储方法调用,局部变量和方法参数等。它以栈的方式进行管理。每当一个方法被调用时,会在栈内存中创建一个称为栈帧的数据结构,用于存储该方法的局部变量、方法参数、返回地址和操作数栈等信息。栈内存的大小是固定的,由虚拟机在启动时设定。栈内存的分配和释放速度非常快,因为它的生命周期与方法的调用和结束相对应,具有很高的效率。需要注意的是,栈内存是线程私有的,每个线程都拥有自己的栈内存空间,线程之间的数据不能直接共享,每个线程都有自己独立的方法调用和局部变量。由于栈内存的特性,它适合存储方法调用、局部变量以及各种基本类型的数据。但是栈内存的空间相对较小,所以当一个线程的栈内存不足时,会抛出Stack OverflowError错误。另外栈内存中的数据在方法调用结束后会被立即释放,因此无法在方法调用之前保留状态信息。

在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后(比如在函数A中调用函数B,在函数B中定义变量a,变量a的作用域只是函数B,在函数B运行完以后,变量a会自动销毁,分配给它的内存会被回收。)Java会自动释放掉为该变量分配的内存空间,改内存空间可以立即被另作他用。

   堆内存是Java虚拟机运行时数据区域之一,用于存储对象实例,所有通过new关键字创建的对象都在堆中分配内存。在java程序运行过程中,堆内存的大小可以通过启动参数来制定,例如-Xms和-Xmx参数分别用于设置Java堆内存的初始大小和最大大小。合理地管理Java堆内存对于程序的性能和稳定性至关重要,如果堆内存不足或者存在内存泄漏,都可能导致程序运行缓慢或崩溃,因此,开发人员需要根据应用的需求进行适当的内存调优和监控。

   用来存放由new创建的对象和数组,在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值为数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外被释放。而数组和对象本身在堆中分配,即使程序运行到使用new产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能再被使用,但依然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。这也是Java比较占内存的原因。

合理管理内存的必要性
资源利用:合理管理内存可以最大程度地利用系统资源。如果没有有效的管理内存,可能会导致内存溢出或频繁的垃圾回收,从而影响程序的性能和可用性
性能优化:通过减少内存占用,可以提高程序的运行效率和响应速度。过多的内存分配会导致频繁的垃圾回收操作,从而增加系统开销和延迟。
避免内存泄漏:不正确或不及时地释放不再使用的对象会导致内存泄漏。如果内存泄漏累计,将会耗尽可用的内存,导致系统崩溃或变得不稳定。
系统稳定性:合理管理内存有助于降低系统崩溃和异常的风险。通过检测和修复内存问题,可以减少因内存错误引起的程序崩溃和不正常的行为。
提高可扩展性:当应用需要处理大量数据或长时间运行时,合理管理内存可以确保系统具有足够的内存来支持应用的需求。这有助于增强系统的可扩展性和稳定性。

为了合理管理内存,开发人员可以采取以下措施:
及时释放不再使用的对象,避免内存泄漏。
使用合适的数据结构和算法,减少内存占用
根据应用需求配置适当的堆大小。
避免创建过多的临时对象,尽量重用对象。
优化代码,减少内存分配和垃圾回收的频率。
使用内存分析工具来检测和修复内存问题。

4、StringBuffer和StringBuilder的区别
线程安全、缓冲区、性能
StringBuffer:线程安全,因为它的所有方法都是synchronized修饰的;每次获取toString都会直接使用缓存区的toStringCache值来构造一个字符串。性能较差,所有公开方法都是同步的。
StringBuilder:线程不安全;每次都需要赋值一次字符数组,再构造一个字符串。性能较好,没有对方法加锁同步。

5、JVM,新生代,老年代,永久代
新生代、老年代、永久代
   新生代:主要是用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。新生代又分为Eden区、SurvivorFrom、SurvivorTo三个区。
Eden区:Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。
SurvivorFrom:保留了一次MinorGC过程中的幸存者。
SurvivorTo:上一次GC的幸存者,作为这一次GC的被扫描者。
当JVM无法为新建对象分配内存空间的时候(Eden满了),MinerGC被触发。因此新生代空间占用率越高,MinorGC越频繁。
MinorGC的过程:采用复制算法。首先,吧Eden和SurvivorFrom区域中存活的对象复制到SurvivorTo区域(如果有对象的年龄以及达到了老年的标准,一般是15,则赋值到老年代区),同时把这些对象的年龄+1(如果SurvivorTo不够位置了就放到老年代区),然后,清空Eden和SurvivorFrom中的对象;最后,SurvivorTo和SurvivorFrom互换,原SurvivorTo称为下一次GC时的SurvivorFrom区。

老年代:老年代的对象比较稳定,所以MajorGC不会频繁执行。在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC进行垃圾回收腾出空间。
MajorGC采用标记-清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。

永久代:只内存的永久保存区域,主要存放Class和Meta(元数据)的信息。Class在被加载的时候被放入永久区域。它和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入native memory,字符串池和类的静态变量放入java堆中,这样可以加载多少类的元数据就不再由MaxPermSize控制,而由系统的实际可用空间来控制。

GC触发机制
MinorGC触发机制:当年清代满时就会触发MinorGC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC。通过复制算法,回收垃圾。复制算法不会产生垃圾碎片。

复制算法:将内存划分为两个区间,在任意时间点,所有动态分配的对象都只能分配在其中一个区间(称为活动区间),而另外一个区间(称为空闲区间)则是空闲的。当有效内存空间耗尽时,JVM将暂停程序运行,开启复制算法GC线程。接下来GC线程会将活动区间内的存活对象,全部复制到空闲区间,且严格按照内存地址依次排列,与此同时,GC线程会将活动区间内的存活对象,全部复制到空闲区间,且严格按照内存地址依次排列,与此同时,GC线程将更新存活对象的内存引用地址指向新的内存地址。此时,空闲区间已经与活动区间交换,而垃圾对象现在已经全部留在了原来的活动区间,也就是现在的空闲区间。事实上,在活动区间转换为空闲区间的同时,垃圾对象已经被一次性全部回收。

MajorGC触发机制:MajorGC又称FullGC。当老年代空间不够用的时候,虚拟机会使用“标记-清除”或者“标记-整理”算法清理出连续的内存空间,分配对象使用。

FullGC触发机制:(1)调用System.gc时,系统间一直想FullGC,但是不必然执行。(2)老年代空间不足。(3)方法区空间不足。(4)通过MinorGC后进入老年代的平均大小大于老年代的可用内存。(5)在Eden区、SurvivorFrom区向SurvivorTo区复制时,对象大小大于ToSpace可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小。当永久代满时也会引发FullGC,会导致Class、Method元信息的卸载。

虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次MinorGC后仍然存活,并且能被Survivor收容的话,将被移动到Survivor空间中,并将对象年龄设为1.对象在Survivor区中每熬过一次MinorGC

年龄就增加1岁,当他的年龄增加到一定程度(默认为15岁)时,就会被晋升到老年代中。对象晋升老年代的的年龄阈值,可以通过参数-XX:Max TenuringRhresholdl来设置。

6、==和equals的区别
==:对于两个基本类型变量,切都是数值类型(不一定要求数据类型严格相同),只要两个变量的值相等,就返回true。对于两个引用类型变量,只有它们指向同一个对象时,才会返回true。不可用于比较类型上没有父子关系的两个对象。

equals:是Object类提供的一个实例方法,所有引用变量都可以调用该方法来判断是否与其他引用变量相等。但使用这个方法判断两个对象相等的标准与使用==运算符没有区别,同样要求两个引用变量指向同一个对象才会返回true。如果希望采用自定义的相等标准,可以重写equals方法来实现。

正确重写equals需要注意:自反性,对称性,传递性,一致性,对任何不是null的x, x.equals(null)一定返回false。
equals()和hashCode():
hashCode():获取哈希码,也称为散列码,返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。在散列表中才会发挥作用,当对象不会用于创建向HashMap、HashSet等集合时,可以不用重写hashCode方法,否则必须重写。一般情况下重写equals方法时也要重写hashCode方法。

hashCode主要用于提升查询效率,提高哈希表性能,来确定散列架构中对象的存储地址。

重写equals方法必须重写hashCode方法

哈慈存储将诶狗中,添加元素重复性校验的标准就是先检查hashCode值,后判断equals()

两个对象equals()相等,hashCode()必然相等

hashCode()不等,equals()必然不等,hashCode()相等,对象不一定相等,需要用equals()进一步判断。

7,对进程和线程的了解

进程是一个具有一定独立功能的程序在一个数据集合上依次动态执行的过程。进程是一个正在执行的程序的实例,包括程序计数器、寄存器和程序变量的当前值。进程依赖于程序运行而存在,是动态的,程序是静态的。进程是操作系统进行资源分配和调度的一个独立单位(CPU除外,线程是处理器任务调度和执行的基本单位);每个进程拥有独立的地址空间,地址空间包括代码区、数据区和堆栈区,进程之间的地址空间是隔离的,互不影响。进程的创建、销毁与切换存在着较大开销,因此需要一种轻量级的进程技术来减少开销。

线程被设计成进程的一个执行路径,同一个进程中的线程共享进程的资源,因此系统对线程的调度所需的成本远远小于进程。

本质区别:进程是操作系统资源分配的基本单位,线程是处理器任务调度和执行的基本单位。

包含关系:一个进程至少有一个线程,线程是进程的一部分,所以线程也被称为轻权进程或轻量级进程。

资源开销:每个进程都有独立的地址空间,进程之间的切换会有较大的开销;同一个进程内的线程共享进程的地址空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小。

影响关系:一个进程崩溃后,在保护模式下其他进程不会被影响,但是一个线程崩溃可能导致整个进程被操作系统杀掉,所以多进程要比多线程健壮。

并发与并行:
并发:cpu在不用线程间切换
并行:多个cpu操作多个线程同时运行。

时间片轮转调度:每个进程会被操作系统分配一个时间片,即每次被CPU选中来执行当前进程所用的时间。时间一到,无论进程是否运行结束,操作系统都会强制将CPU这个资源转到另一个进程去执行。

8,想问的问题

C 0条回复 评论

帖子还没人回复快来抢沙发