每日十题八股-2024年12月13日

1.程序计数器的作用,为什么是私有的?
2.方法区中的方法的执行过程?
3.方法区中还有哪些东西?
4.String保存在哪里呢?
5.引用类型有哪些?有什么区别?
6.弱引用了解吗?举例说明在哪里可以用?
7.内存泄漏和内存溢出的理解?
8.jvm 内存结构有哪几种内存溢出的情况?
9.有具体的内存泄漏和内存溢出的例子么请举例及解决方案?
10.创建对象的过程?

1.程序计数器的作用,为什么是私有的?

记录当前线程运行到的指令行号,使得线程的运行独立且便于线程之间的切换能够快速定位。
在这里插入图片描述

2.方法区中的方法的执行过程?

答案:

https://juejin.cn/post/7081620160914096159
方法区和永久代以及元空间的关系很像 Java 中接口和类的关系,类实现了接口,这里的类就可以看作是永久代和元空间,接口可以看作是方法区,也就是说永久代以及元空间是 HotSpot 虚拟机对虚拟机规范中方法区的两种实现方式。为什么要用元空间替换永久代呢?因为永久代的存储受限于JVM本身的固定大小,而元空间直接使用本地内存,受限于本机内存,这样元空间溢出的概率极大的降低了。

方法区属于是 JVM 运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。
当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

3.方法区中还有哪些东西?

当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

运行时常量池:存储着在类文件中的常量池数据,在类加载后在方法区生成该运行时常量池。

4.String保存在哪里呢?

保存在堆中。堆中保存的都是对象实例和数组。
String 保存在字符串常量池中,不同于其他对象,它的值是不可变的,且可以被多个引用共享。
字符串常量池,jdk1.7之前在方法区,1.7在堆中,之后永久代改为元空间,就又回到了堆中。

5.引用类型有哪些?有什么区别?

在 Java 中,基本类型和引用类型是两种不同的数据类型。
从JDK 1.2版本开始,对象的引用被划分为4种级别,**从而使程序能更加灵活地控制对象的生命周期。**这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
在这里插入图片描述

强引用不会被垃圾回收,除非手动弱化;软引用在内存不足时回收,可用于内存敏感的高速缓存;弱引用不管内存是否充足,垃圾回收时都会回收;虚引用不决定对象生命周期,主要用于跟踪对象回收。

强引用指的就是代码中普遍存在的赋值方式,比如A a = new A()这种。强引用关联的对象,永远不会被GC回收。

SoftReference<Object> softRef = new SoftReference<Object>(obj); // 软引用
WeakReference<String> weakReference = new WeakReference<>(obj);// 弱引用
PhantomReference phantomRef = new PhantomReference(obj); // 虚引用

6.弱引用了解吗?举例说明在哪里可以用?

弱引用不管内存是否充足,垃圾回收时都会回收。

WeakReference<String> weakReference = new WeakReference<>(obj);// 弱引用

缓存系统:弱引用常用于实现缓存,特别是当希望缓存项能够在内存压力下自动释放时。
避免内存泄露:当一个对象不应该被长期引用时,使用弱引用可以防止该对象被意外地保留。

7.内存泄漏和内存溢出的理解?

内存泄漏:程序运行中有不再被使用的对象(但仍然被引用持有),这样GC无法回收,会导致内存不断减少。ThreadLocal如果没有remove,当线程结束时,就会造成内存泄漏。
内存溢出:内存溢出是指Java虚拟机(JVM)在申请内存时,无法找到足够的内存。OutOfMemoryError这通常发生在堆内存不足以存放新创建的对象时。如,深度递归或大量的对象创建超出堆内存。

8.jvm 内存结构有哪几种内存溢出的情况?

堆内存溢出:大量的对象被创建,或者发生内存泄漏,会导致堆的存储空间不足以应对。
栈溢出:这在递归的时候容易发生,不断创建栈帧。
元空间溢出:项目的代码量过大,依赖库多等等,造成本地内存无法应对。
直接内存内存溢出:在使用ByteBuffer中的allocateDirect()的时候会用到。OutOfMemoryError: Direct buffer memory。??(这个不理解)

9.有具体的内存泄漏和内存溢出的例子么?请举例及解决方案?

静态变量导致内存泄露。在Java中,静态属性的生命周期通常伴随着应用整个生命周期(除非ClassLoader符合垃圾回收的条件)。(第一,进来减少静态变量;第二,如果使用单例,尽量采用懒加载。)

未关闭的资源。数据库链接、输入流和session对象。

使用ThreadLocal。

10.创建对象的过程?

1.虚拟机遇到一条new指令时,首先会去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且会检查这个符号引用所指向的类是否已经完成加载、连接和初始化,如果没有,必须先执行相应类的类加载过程。
2.类加载之后,进行内存分配,即在堆中划分一块内存供这个对象实例使用。
3.除了对象头,虚拟机需要将新分配的内存空间都初始化为零值。
4.设置对象头数据,例如类的元数据信息,对象的哈希码,对象的GC分代年龄。
5.执行执行init方法。

11.补充:ThreadLocal到底是怎么造成内存泄漏的?

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
简单来说:JVM创建的对象永远都无法访问到,但是GC又不能回收对象所占用的内存。
ThreadLocal是一个类。
Thread是一个类。
ThreadLocalMap是Thread里的一个内部类。
调用ThreadLocal实例的set,就会以ThreadLocal自身的引用和value存入Thread类的ThreadLocalMap里。

ThreadLocalMap内部维护了一个Entry[] table来存储键值对的映射关系,Entry将ThreadLocal作为Key,值作为value保存,它继承自WeakReference,注意构造函数里的第一行代码super(k),这意味着ThreadLocal对象是一个「弱引用」。

综上所述,由于**ThreadLocal对象是弱引用,如果外部没有强引用指向它,它就会被GC回收,导致Entry的Key为null,如果这时value外部也没有强引用指向它,那么value就永远也访问不到了,按理也应该被GC回收,但是由于Entry对象还在强引用value,导致value无法被回收,这时「内存泄漏」就发生了,**value成了一个永远也无法被访问,但是又无法被回收的对象。

Entry对象属于ThreadLocalMap,ThreadLocalMap属于Thread,如果线程本身的生命周期很短,短时间内就会被销毁,那么「内存泄漏」立刻就会得到解决,只要线程被销毁,value也会随之被回收。问题是,线程本身是非常珍贵的计算机资源,很少会去频繁的创建和销毁,一般都是通过线程池来使用,这就将线程的生命周期大大拉长,「内存泄漏」的影响也会越来越大。

那么不要弱引用就可以避免内存泄漏吗?弱引用是ThreadLocal来避免「内存泄漏」的,而不是导致内存泄漏的元凶。

使用ThreadLocal时,一般建议将其声明为static final的,避免频繁创建ThreadLocal实例。 尽量避免存储大对象,如果非要存,那么尽量在访问完成后及时调用remove()删除掉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值