技术提升扩展1

1、问答

1、什么情况会出现内存溢出,内存泄露?

内存泄漏memory leak :是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。

Java内存泄漏的根本原因是什么呢?长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏,尽管短生命周期对象已经不再需要,但是因为长生命周期持有它的引用而导致不能被回收,这就是Java中内存泄漏的发生场景。具体主要有如下几大类:

1、静态集合类引起内存泄漏:
像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们也将一直被Vector等引用着,循环申请Object 对象,并将所申请的对象放入一个Vector 中,如果仅仅释放引用本身(o=null),那么Vector 仍然引用该对象,所以这个对象对GC 来说是不可回收的。因此,如果对象加入到Vector 后,还必须从Vector 中删除,最简单的方法就是将Vector对象设置为null。
2、当集合里面的对象属性被修改后,再调用remove()方法时不起作用:
原因是对象属性改变以后hashcode变了
3、监听器
在java 编程中,我们都需要和监听器打交道,通常一个应用当中会用到很多监听器,我们会调用一个控件的诸如addXXXListener()等方法来增加监听器,但往往在释放对象的时候却没有记住去删除这些监听器,从而增加了内存泄漏的机会。
4、各种连接
比如数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的。对于Resultset 和Statement 对象可以不进行显式回收,但Connection 一定要显式回收,因为Connection 在任何时候都无法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会立即为NULL。但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。这种情况下一般都会在try里面去的连接,在finally里面释放连接。
5、内部类和外部模块的引用
内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。此外程序员还要小心外部模块不经意的引用,例如程序员A 负责A 模块,调用了B 模块的一个方法如:public void registerMsg(Object b);这种调用就要非常小心了,传入了一个对象,很可能模块B就保持了对该对象的引用,这时候就需要注意模块B 是否提供相应的操作去除引用。
6、单例模式
不正确使用单例模式是引起内存泄漏的一个常见问题,单例对象在初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部的引用,那么这个对象将不能被JVM正常回收,导致内存泄漏

内存溢出 out of memory :指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。

1、Java堆溢出:
触发方法:将内存设置为较小的值,然后一直new对象,或者直接new一个大于内存大小的byte数组。
解决:首先确认内存中的对象是否是必要的,也就是首先分清楚到底是出现了内存泄漏还是内存溢出。 如果是内存泄漏就找到具体代码,如果是内存溢出就检查内存是否可以再扩大,从代码上检查是否存在某些对象生命周期过长,持有状态时间过长的情况。
.2、虚拟机栈和本地方法栈溢出
StackOverflowError异常通过设置栈内存大小,然后递归调用方法可以触发
OutOfmemoryError异常通过一直创建线程触发,这个在电脑上没有实验成功,电脑会假死,但程序没有抛出异常
3、方法区溢出
触发方式:运行时产生大量的类去填充方法区,直至溢出
运行时常量池溢出
触发方式:循环创建字符串,但是在实验中发现抛出的是堆溢出
4、本机直接内存溢出

2、悲观锁和乐观锁是什么,如何实现,有哪些方式实现?

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制, 比如行锁,表锁等,读锁, 写锁等,都是在做操作之前先上锁。再比如Java 里面的同步原语synchronized 关键字的实现也是悲观锁。

乐观锁:顾名思义,就是很乐观, 每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition 机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式CAS 实现的。

乐观锁的实现方式:

1、使用版本标识来确定读到的数据与提交时的数据是否一致。提交后修改版本标识, 不一致时可以采取丢弃和再次尝试的策略。

2、java 中的Compare and Swap 即CAS ,当多个线程尝试使用CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败, 失败的线程并不会被挂起,而是被告知这次竞争中失败, 并可以再次尝试。CAS 操作中包含三个操作数—— 需要读写的内存位置( V)、进行比较的预期原值( A)
和拟写入的新值(B)。如果内存位置V 的值与预期原值A 相匹配,那么处理器会自动将该位置值更新为新值B。否则处理器不做任何操作。

CAS 缺点:

1、ABA 问题:
比如说一个线程one 从内存位置V 中取出A,这时候另一个线程two 也从内存中取出A,并且two 进行了一些操作变成了B,然后two 又将V 位置的数据变成A,这时候线程one 进行CAS 操作发现内存中仍然是A,然后one 操作成功。尽管线程one 的CAS 操作成功,但可能存在潜藏的问题。从Java1.5 开始JDK 的atomic包里提供了一个类AtomicStampedReference 来解决ABA 问题。

2、循环时间长开销大:
对于资源竞争严重(线程冲突严重) 的情况, CAS 自旋的概率会比较大, 从而浪费更多的CPU 资源,效率低于synchronized。

3、只能保证一个共享变量的原子操作:
当对一个共享变量执行操作时,我们可以使用循环CAS 的方式来保证原子操作,但是对多个共享变量操作时,循环CAS 就无法保证操作的原子性, 这个时候就可以用锁。

3、线程与进程的区别?

进程
一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。

线程
进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。

与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

进程与线程的区别总结

线程具有许多传统进程所具有的特征,故又称为轻型进程(Light—Weight Process)或进程元;而把传统的进程称为重型进程(Heavy—Weight Process),它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少包含一个线程。

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

资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的

影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行

4、Java死锁以及如何避免?

一、什么是死锁

java中的死锁是一种编程情况,其中两个或多个线程被永久阻塞,Java死锁情况出现至少两个线程和两个或更多资源。

在这里,我们将写了一个简单的程序,它将导致java死锁场景,然后我们将分析它。

public class ThreadDeadlock {
   
    public static void main(String[] args) {
   
        Object obj1 = new Object();
        Object obj2 = new Object();

        Thread t1 = new Thread(new SyncThread(obj1, obj2), "t1");
        Thread t2 = new Thread(new SyncThread(obj2, obj1), "t2")
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值