大家好,又见面了,我是你们的朋友全栈君。
发现网上很多Java面试题都没有答案,所以花了很长时间搜集整理出来了这套Java面试题大全,希望对大家有帮助哈~
Java面试永远是程序员迈向成功的第一个门槛,想要面试成功,各种面试题的洗礼是必不可少的,下面就来看看小编精心整理的一些Java工程师面试题及答案吧。
博主已将以下这些面试题整理成了一个Java面试手册,是PDF版的。
Java 中,int 类型变量的长度是一个固定值,与平台无关,都是 32 位。意思就是说,在 32 位 和 64 位 的 Java 虚拟机中,int 类型的长度是相同的。
1、 新建(new):新创建了一个线程对象。
2、 就绪(可运行状态)(runnable):线程对象创建后,当调用线程对象的 start()方法,该线程处于就绪状态,等待被线程调度选中,获取cpu的使用权。
3、 运行(running):可运行状态(runnable)的线程获得了cpu时间片(timeslice),执行程序代码。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
4、 阻塞(block):处于运行状态中的线程由于某种原因,暂时放弃对 CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被 CPU 调用以进入到运行状态。
阻塞的情况分三种:
1、 等待阻塞:
运行状态中的线程执行 wait()方法,JVM会把该线程放入等待队列(waitting queue)中,使本线程进入到等待阻塞状态;
2、 同步阻塞:
线程在获取 synchronized 同步锁失败(因为锁被其它线程所占用),,则JVM会把该线程放入锁池(lock pool)中,线程会进入同步阻塞状态;
3、 其他阻塞:
通过调用线程的 sleep()或 join()或发出了 I/O 请求时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。
4、 死亡(dead)(结束):
线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
1、 应用层
2、 表示层
3、 会话层
4、 传输层
5、 网络层
6、 数据链路层
7、 物理层
8、 (物理层是最底层,应用层是最高层)
OSGi 旨在为实现 Java 程序的模块化编程提供基础条件,基于 OSGi 的程序很可能可以实现模块级的热插拔功能,当程序升级更新时,可以只停用、重新安装然后启动程序的其中一部分,这对企业级程序开发来说是非常具有诱惑力的特性。
OSGi 描绘了一个很美好的模块化开发目标,而且定义了实现这个目标的所需要服务与架构,同时也有成熟的框架进行实现支持。但并非所有的应用都适合采用 OSGi 作为基础架构,它在提供强大功能同时,也引入了额外的复杂度,因为它不遵守了类加载的双亲委托模型。
可以,你可以写一个自己的容器类。如果你想使用 Java 中增强的循环来遍历,你只需要实现 Iterable 接口。如果你实现 Collection 接口,默认就具有该属性。
private static boolean flag = false; private Singleton() { if (flag == false) { flag = !flag; } else { throw new RuntimeException(“单例模式被侵犯!”); } } public static void main(String[] args) { }
Java 8 为例
日志
1、 -XX:+PrintFlagsFinal,打印JVM所有参数的值
2、 -XX:+PrintGC,打印GC信息
3、 -XX:+PrintGCDetails,打印GC详细信息
4、 -XX:+PrintGCTimeStamps,打印GC的时间戳
5、 -Xloggc:filename,设置GC log文件的位置
6、 -XX:+PrintTenuringDistribution,查看熬过收集后剩余对象的年龄分布信息
内存设置
1、 -Xms,设置堆的初始化内存大小
2、 -Xmx,设置堆的最大内存
3、 -Xmn,设置新生代内存大小
4、 -Xss,设置线程栈大小
5、 -XX:NewRatio,新生代与老年代比值
6、 -XX:SurvivorRatio,新生代中Eden区与两个Survivor区的比值,默认为8,即Eden:Survivor:Survivor=8:1:1
7、 -XX:MaxTenuringThreshold,从年轻代到老年代,最大晋升年龄。CMS 下默认为 6,G1 下默认为 15
8、 -XX:MetaspaceSize,设置元空间的大小,第一次超过将触发 GC
9、 -XX:MaxMetaspaceSize,元空间最大值
10、 -XX:MaxDirectMemorySize,用于设置直接内存的最大值,限制通过 DirectByteBuffer 申请的内存
11、 -XX:ReservedCodeCacheSize,用于设置 JIT 编译后的代码存放区大小,如果观察到这个值有限制,可以适当调大,一般够用即可
设置垃圾收集相关
1、 -XX:+UseSerialGC,设置串行收集器
2、 -XX:+UseParallelGC,设置并行收集器
3、 -XX:+UseConcMarkSweepGC,使用CMS收集器
4、 -XX:ParallelGCThreads,设置Parallel GC的线程数
5、 -XX:MaxGCPauseMillis,GC最大暂停时间 ms
6、 -XX:+UseG1GC,使用G1垃圾收集器
CMS 垃圾回收器相关
1、 -XX:+UseCMSInitiatingOccupancyOnly
2、 -XX:CMSInitiatingOccupancyFraction,与前者配合使用,指定MajorGC的发生时机
3、 -XX:+ExplicitGCInvokesConcurrent,代码调用 System.gc() 开始并行 FullGC,建议加上这个参数
4、 -XX:+CMSScavengeBeforeRemark,表示开启或关闭在 CMS 重新标记阶段之前的清除(YGC)尝试,它可以降低 remark 时间,建议加上
5、 -XX:+ParallelRefProcEnabled,可以用来并行处理 Reference,以加快处理速度,缩短耗时
G1 垃圾回收器相关
1、 -XX:MaxGCPauseMillis,用于设置目标停顿时间,G1 会尽力达成
2、 -XX:G1HeapRegionSize,用于设置小堆区大小,建议保持默认
3、 -XX:InitiatingHeapOccupancyPercent,表示当整个堆内存使用达到一定比例(默认是 45%),并发标记阶段就会被启动
4、 -XX:ConcGCThreads,表示并发垃圾收集器使用的线程数量,默认值随 JVM 运行的平台不同而变动,不建议修改
建议面试时最好能记住 CMS 和 G1的参数,特点突出使用较多,被问的概率大
面向对象的特征主要有以下几个方面:
1、 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
2、 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段(如果不能理解请阅读阎宏博士的《Java与模式》或《设计模式精解》中关于桥梁模式的部分)。
3、 封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口(可以想想普通洗衣机和全自动洗衣机的差别,明显全自动洗衣机封装更好因此操作起来更简单;我们现在使用的智能手机也是封装得足够好的,因为几个按键就搞定了所有的事情)。
4、 多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当A系统访问B系统提供的服务时,B系统有多种提供服务的方式,但一切对A系统来说都是透明的(就像电动剃须刀是A系统,它的供电系统是B系统,B系统可以使用电池供电或者用交流电,甚至还有可能是太阳能,A系统只会通过B类对象调用供电的方法,但并不知道供电系统的底层实现是什么,究竟通过何种方式获得了动力)。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:1)、方法重写(子类继承父类并重写父类中已有的或抽象的方法);2)、对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
数据库连接是非常消耗资源的,影响到程序的性能指标。连接池是用来分配、管理、释放数据库连接的,可以使应用程序重复使用同一个数据库连接,而不是每次都创建一个新的数据库连接。通过释放空闲时间较长的数据库连接避免数据库因为创建太多的连接而造成的连接遗漏问题,提高了程序性能。
当你将你的应用从 32 位的 JVM 迁移到 64 位的 JVM 时,由于对象的指针从32 位增加到了 64 位,因此堆内存会突然增加,差不多要翻倍。这也会对 CPU缓存(容量比内存小很多)的数据产生不利的影响。因为,迁移到 64 位的 JVM主要动机在于可以指定最大堆大小,通过压缩OOP 可以节省一定的内存。通过-XX:+UseCompressedOops 选项,JVM 会使用 32 位的 OOP,而不是 64 位的 OOP。
1、 服务器程序创建一个ServerSocket,然后再用accept方法等待客户来连接
2、 客户端程序创建一个Socket并请求与服务器建立连接
3、 服务器接收客户的连接请求,并创建一个新的Socket与该客户建立专线连接
4、 刚才建立了连接的两个Socket在一个线程上对话
5、 服务器开始等待新的连接请求
虽然两者都是用来暂停当前运行的线程,但是 sleep() 实际上只是短暂停顿,因为它不会释放锁,而 wait() 意味着条件等待,这就是为什么该方法要释放锁,因为只有这样,其他等待的线程才能在满足条件时获取到该锁。
一块较小的内存空间, 是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有” 的内存。
正在执行 java 方法的话,计数器记录的是虚拟机字节码指令的地址(当前指令的地址) 。如果还是 Native 方法,则为空。
这个内存区域是唯一一个在虚拟机中没有规定任何 OutOfMemoryError 情况的区域。
线程调度器是一个操作系统服务,它负责为Runnable状态的线程分配CPU时间。一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间可以基于线程优先级或者线程等待的时间。线程调度并不受到Java虚拟机控制,所以由应用程序来控制它是更好的选择(也就是说不要让你的程序依赖于线程的优先级)。
Iterator 接口提供遍历任何 Collection 的接口。我们可以从一个 Collection 中使用迭代器方法来获取迭代器实例。迭代器取代了 Java 集合框架中的 Enumeration,迭代器允许调用者在迭代过程中移除元素。
因为所有Collection接继承了Iterator迭代器
1、 sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
2、 线程执行 sleep()方法后转入阻塞(blocked)状态,而执行 yield()方法后转入就绪(ready)状态;
3、 sleep()方法声明抛出 InterruptedException,而 yield()方法没有声明任何异常;
4、 sleep()方法比 yield()方法(跟操作系统 CPU 调度相关)具有更好的可移植性,通常不建议使用yield()方法来控制并发线程的执行。
能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组。意思是,如果改变引用指向的数组,将会受到 volatile 的保护,但是如果多个线程同时改变数组的元素,volatile 标示符就不能起到之前的保护作用了。
tech.souyunku.com/bluestorm/a…
使用{}实例化一个json对象,json对象多个元素使用逗号隔开,每个元素都是一个键值对
引用计数:在对象中添加一个引用计数器,如果被引用计数器加 1,引用失效时计数器减 1,如果计数器为 0 则被标记为垃圾。原理简单,效率高,但是在 Java 中很少使用,因为存在对象间循环引用的问题,导致计数器无法清零。
可达性分析:主流语言的内存管理都使用可达性分析判断对象是否存活。基本思路是通过一系列称为 GC Roots 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程走过的路径称为引用链,如果某个对象到 GC Roots 没有任何引用链相连,则会被标记为垃圾。可作为 GC Roots 的对象包括虚拟机栈和本地方法栈中引用的对象、类静态属性引用的对象、常量引用的对象。
抽象工厂模式:通常由工厂方法模式来实现。但一个工厂中往往含有多个工厂方法生成一系列的产品。这个模式强调的是客户代码一次保证只使用一个系列的产品。当要切换为另一个系列的产品,换一个工厂类即可。
原型模式:工厂方法的最大缺点就是,对应一个继承体系的产品类,要有一个同样复杂的工厂类的继承体系。我们可以把工厂类中的工厂方法放到产品类自身之中吗?如果这样的话,就可以将两个继承体系为一个。这也就是原型模式的思想,原型模式中的工厂方法为clone,它会返回一个拷贝(可以是浅拷贝,也可以是深拷贝,由设计者决定)。为了保证用户代码中到时可以通过指针调用clone来动态绑定地生成所需的具体的类。这些原型对象必须事先构造好。
原型模式想对工厂方法模式的另一个好处是,拷贝的效率一般对构造的效率要高。
出现线程安全问题的原因一般都是三个原因:
1、 线程切换带来的原子性问题 解决办法:使用多线程之间同步synchronized或使用锁(lock)。
2、 缓存导致的可见性问题 解决办法:synchronized、volatile、LOCK,可以解决可见性问题
3、 编译优化带来的有序性问题 解决办法:Happens-Before 规则可以解决有序性问题
一种实践是用 volatile 修饰 long 和 double 变量,使其能按原子类型来读写。double 和 long 都是64位宽,因此对这两种类型的读是分为两部分的,第一次读取第一个 32 位,然后再读剩下的 32 位,这个过程不是原子的,但 Java 中 volatile 型的 long 或 double 变量的读写是原子的。volatile 修复符的另一个作用是提供内存屏障(memory barrier),例如在分布式框架中的应用。简单的说,就是当你写一个 volatile 变量之前,Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量之前,会插入一个读屏障(read barrier)。意思就是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值,同时,在写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其他所有写的值更新到缓存。
1、 Byte,short,int,long默认是都是0
2、 Boolean默认值是false
3、 Char类型的默认值是’’
4、 Float与double类型的默认是0.0
5、 对象类型的默认值是null
在 Java 中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在 JVM 中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。
有可能,两个不相等的对象可能会有相同的 hashcode 值,这就是为什么在 hashmap 中会有冲突。相等 hashcode 值的规定只是说如果两个对象相等,必须有相同的hashcode 值,但是没有关于不相等对象的任何规定。
1、 首先synchronized是Java内置关键字,在JVM层面,Lock是个Java类;
2、 synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
3、 synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。
4、 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,访问它比访问ArrayList慢很多
ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。ArrayList的缺点是每个元素之间不能有间隔。
建立对象就是为了使用对象,我们的Java程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式有虚拟机实现而定,目前主流的访问方式有使用句柄和直接指针2种:
句柄:如果使用句柄的话,那么Java堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。
直接指针:如果使用直接指针访问,那么 Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference 中存储的直接就是对象的地址。
这两种对象访问方式各有优势。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。使用直接指针访问方式最大的好处就是速度快,它节省了一次指针定位的时间开销。
通俗点讲:是看看左右是不是一个东西。equals是看看左右是不是长得一样。如何记住嘛。如果单纯是想记住,:等于。equals:相同。两个长得一样的人,只能说长的相同(equals),但是不等于他们俩是一个人。你只要记住equals,==就不用记了。
术语来讲的区别:
1、 ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同
2、 ==是指对内存地址进行比较 equals()是对字符串的内容进行比较3.==指引用是否相同 equals()指的是值是否相同
判断一个对象是否存活有两种方法:
1、 引用计数法
所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一。当一个对象的引用计数器为零时,说明此对象没有被引用,也就是“死对象”,将会被垃圾回收、
引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象 A 引用对象 B,对象 B 又引用者对象 A,那么此时 A、B 对象的引用计数器都不为零,也就造成无法完成垃圾回收,所以主流的虚拟机都没有采用这种算法。
2、 可达性算法(引用链法)
该算法的思想是:从一个被称为 GC Roots 的对象开始向下搜索,如果一个对象到 GC Roots 没有任何引用链相连时,则说明此对象不可用。
在 Java 中可以作为 GC Roots 的对象有以下几种:
1、 虚拟机栈中引用的对象
2、 方法区类静态属性引用的对象
3、 方法区常量池引用的对象
4、 本地方法栈JNI引用的对象
虽然这些算法可以判定一个对象是否能被回收,但是当满足上述条件时,一个对象比不一定会被回收。当一个对象不可达 GC Root 时,这个对象并不会立马被回收,而是出于一个死缓的阶段,若要被真正的回收需要经历两次标记、
如果对象在可达性分析中没有与 GC Root 的引用链,那么此时就会被第一次标记并且进行一次筛选,筛选的条件是是否有必要执行 finalize() 方法。当对象没有覆盖 finalize() 方法或者已被虚拟机调用过,那么就认为是没必要的。 如果该对象有必要执行 finalize() 方法,那么这个对象将会放在一个称为 F-Queue 的对队列中,虚拟机会触发一个 Finalize() 线程去执行,此线程是低优先级的,并且虚拟机不会承诺一直等待它运行完,这是因为如果 finalize() 执行缓慢或者发生了死锁,那么就会造成 F-Queue 队列一直等待,造成了内存回收系统的崩溃。GC 对处于 F-Queue 中的对象进行第二次被标记,这时,该对象将被移除” 即将回收” 集合,等待回收。
Integer是int的包装类型。
Int的默认值是0,integer的默认值是null
1、 加载:判断servlet实例是否存在,如果不存在,就加载serlvet
2、 实例化:
3、 初始化
4、服务
5、销毁
如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。
优点:
1、 保证性能下限: 虚拟DOM可以经过diff找出最小差异,然后批量进行patch,这种操作虽然比不上手动优化,但是比起粗暴的DOM操作性能要好很多,因此虚拟DOM可以保证性能下限
2、 无需手动操作DOM: 虚拟DOM的diff和patch都是在一次更新中自动进行的,我们无需手动操作DOM,极大提高开发效率
3、 跨平台: 虚拟DOM本质上是JavaScript对象,而DOM与平台强相关,相比之下虚拟DOM可以进行更方便地跨平台操作,例如服务器渲染、移动端开发等等
缺点:
无法进行极致优化: 在一些性能要求极高的应用中虚拟DOM无法进行针对性的极致优化,比如VScode采用直接手动操作DOM的方式进行极端的性能优化
类加载器具有等级制度但非继承关系,以组合的方式复用父加载器的功能。双亲委派模型要求除了顶层的启动类加载器外,其余类加载器都应该有自己的父加载器。
一个类加载器收到了类加载请求,它不会自己去尝试加载,而将该请求委派给父加载器,每层的类加载器都是如此,因此所有加载请求最终都应该传送到启动类加载器,只有当父加载器反馈无法完成请求时,子加载器才会尝试。
类跟随它的加载器一起具备了有优先级的层次关系,确保某个类在各个类加载器环境中都是同一个,保证程序的稳定性。
Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化,其语法看起来挺诡异的,如下所示。
/ * 扑克类(一副扑克) * @author 骆昊 * */
public class Poker {
private static String[] suites = {
"黑桃", "红桃", "草花", "方块"
};
private static int[] faces = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
};
private Card[] cards;
/ * 构造器 * */
public Poker() {
cards = new Card[52];
for (int i = 0; i < suites.length; i++) {
for (int j = 0; j < faces.length; j++) {
cards[i * 13 + j] = new Card(suites[i], faces[j]);
}
}
}
/ * 洗牌 (随机乱序) * */
public void shuffle() {
for (int i = 0, len = cards.length; i < len; i++) {
int index = (int)(Math.random() * len);
Card temp = cards[index];
cards[index] = cards[i];
cards[i] = temp;
}
}
/ * 发牌 * @param index 发牌的位置 * */
public Card deal(int index) {
return cards[index];
}
/ * 卡片类(一张扑克) * [内部类] * @author 骆昊 * */
public class Card {
private String suite; // 花色 private int face; // 点数
public Card(String suite, int face) { this.suite = suite;
this.face = face; } @Override
public String toString() { String faceStr = ""; switch(face) { case 1: faceStr = "A"; break; case 11: faceStr = "J"; break; case 12: faceStr = "Q"; break; case 13: faceStr = "K"; break; default: faceStr = String.valueOf(face); } return suite + faceStr; } } }
测试代码:
class PokerTest {
public static void main(String[] args) {
Poker poker = new Poker();
poker.shuffle();
// 洗牌 Poker.Card c1 = poker.deal(0); // 发第一张牌
// 对于非静态内部类Card // 只有通过其外部类Poker对象才能创建Card对象
Poker.Card c2 = poker.new Card("红心", 1); // 自己创建一张牌
System.out.println(c1); // 洗牌后的第一张 System.out.println(c2);
// 打印: 红心A } }
面试题 – 下面的代码哪些地方会产生编译错误?
class Outer { class Inner {} public static void foo() { new Inner(); } public void bar() { new Inner(); } public static void main(String[] args) { new Inner(); } }
注意:Java中非静态内部类对象的创建要依赖其外部类对象,上面的面试题中foo和main方法都是静态方法,静态方法中没有this,也就是说没有所谓的外部类对象,因此无法创建内部类对象,如果要在静态方法中创建内部类对象,可以这样做:
new Outer().new Inner();
1、 不会,在下一个垃圾回调周期中,这个对象将是被可回收的。
2、 也就是说并不会立即被垃圾收集器立刻回收,而是在下一次垃圾回收时才会释放其占用的内存。
这题就依据full GC的触发条件来做:
1、 如果有perm gen的话(jdk1.8就没了),要给perm gen分配空间,但没有足够的空间时,会触发full gc。
2、 所以看看是不是perm gen区的值设置得太小了。
3、 System.gc()
方法的调用
4、 这个一般没人去调用吧~~~
5、 当统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间,则会触发full gc(这就可以从多个角度上看了)
6、 是不是频繁创建了大对象(也有可能eden区设置过小)(大对象直接分配在老年代中,导致老年代空间不足—>从而频繁gc)
7、 是不是老年代的空间设置过小了(Minor GC几个对象就大于老年代的剩余空间了)
JVM 内存区域主要分为线程私有区域【程序计数器、虚拟机栈、本地方法区】、线程共享区域【JAVA 堆、方法区】、直接内存。
线程私有数据区域生命周期与线程相同, 依赖用户线程的启动/结束 而 创建/销毁(在 Hotspot VM 内, 每个线程都与操作系统的本地线程直接映射, 因此这部分内存区域的存/否跟随本地线程的生/死对应)。
线程共享区域随虚拟机的启动/关闭而创建/销毁。
直接内存并不是 JVM 运行时数据区的一部分, 但也会被频繁的使用: 在 JDK 1.4 引入的 NIO 提供了基于Channel与 Buffer的IO方式, 它可以使用Native函数库直接分配堆外内存, 然后使用DirectByteBuffer 对象作为这块内存的引用进行操作(详见: Java I/O 扩展), 这样就避免了在 Java堆和 Native 堆中来回复制数据, 因此在一些场景中可以显著提高性能。
01、如何判断一个对象是否存活
04、怎么唤醒一个阻塞的线程
05、虚拟DOM的优劣如何?
06、双亲委派模型是什么?
07、静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同?
08、如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?
09、JVM 出现 fullGC 很频繁,怎么去线上排查问题
10、JVM 内存区域
11、Parallel Old 收集器(多线程标记整理算法)
13、当一个线程进入某个对象的一个synchronized的实例方法后,其它线程是否可进入此对象的其它方法?
14、Serial 与 Parallel GC 之间的不同之处?
15、为什么线程通信的方法 wait(), notify()和 notifyAll()被定义在 Object 类里?
17、类加载为什么要使用双亲委派模式,有没有什么场景是打破了这个模式?
18、Hibernate中Session的load和get方法的区别是什么?
23、线程与进程的区别?
24、Session的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法分别是做什么的?有什么区别?
25、用代码演示三种代理
26、stackoverflow错误,permgen space错误
27、分代收集算法
32、如何实现对象克隆?
34、如何通过反射创建对象?
36、volatile 能使得一个非原子操作变成原子操作吗?
38、你能写出一个正则表达式来判断一个字符串是否是一个数字吗?
39、运行时栈帧包含哪些结构?
40、什么是Java程序的主类?应用程序和小程序的主类有何不同?
45、线程池有什么优点?
50、Java网络编程有几种?
51、js如何实现页面刷新呢?
52、什么是线程池?
55、为什么线程通信的方法wait(), notify()和notifyAll()被定义在Object 类里?
56、遍历一个 List 有哪些不同的方式?每种方法的实现原理是什么?Java 中 List 遍历的最佳实践是什么?
57、String str=”i”与 String str=new String(“i”)一样吗?
58、用过ConcurrentHashMap,讲一下他和HashTable的不同之处?
60、线程池中 submit() 和 execute() 方法有什么区别?
62、CMS分为哪几个阶段?
03、抽象的关键字是什么?
64、如何判断对象可以被回收
66、如何进行单元测试
68、什么是Hash算法
69、什么是上下文切换?
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/156657.html原文链接:https://javaforall.cn