2/17

1.final、finally、finalize,final能不能保证线程安全
final 可以用来修饰类、方法、变量,分别有不同的意义,final 修饰的 class 代表不可以继承扩展,final 的变量是不可以修改的,而 final 的方法也是不可以重写的。
finally 则是 Java 保证重点代码一定要被执行的一种机制。我们可以使用 try-finally 或者 try-catch-finally 来进行类似关闭 Java数据库连接、保证 unlock 锁等动作。
finalize 是基础类 java.lang.Object 的一个方法,它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。
final仅能提供部分线程安全。
1)final可以保证正在创建中的对象 不能被其他线程访问到。
2)如果有一个对collection的final引用,array 或者其他可变的对象(可以添加元素呗像下面那样的),在多线程访问的情况下仍然需要同步机制锁等,(或者可以使用线程安全的类,如CurrentHashMap)
3)当声明一个final的引用时, 只能说明该引用是不可变的,但是数据是可变的。比如声明一个final 的list:private final List myList = new ArrayList();你仍然可以改变这个list:myList.add(“Hello”);
2.static和内存泄露问题
static变量所指向的内存引用,如果不把它设置为null,GC是永远不会回收这个对象的。所以为了让GC能够回收static变量 我们在使用完后将static变量设置为null。
其生命周期将与整个app进程生命周期一样。这会导致一系列问题,如果你的app进程设计上是长驻内存的,那即使app切到后台,这部分内存也不会被释放。可以使用懒加载
3.jvm创建一个对象的的过程;
①首先将去检查这个指令的参数是否能在常量池定位到一个类的符号引用并且检查 这个符号引用代表的类是否已被加载、连接和初始化过。
②如果没有,那必须先执行相应的类加载过程(加载 链接 初始化)
其中初始化包括:验证、准备、解析
③加载完成后,接下来为新生对象在堆中分配内存(对象所需内存的大小在类加载完成后便可完全确定)。具体的堆分配方式有两种:一种是指针碰撞,一种是空闲链表。
指针碰撞:用于java堆内存绝对规整,指针的一边是使用过的,另一边是未使用
空闲链表:用于堆内存不规整,需要维持一个列表记录哪块内存可用。
注意:
选择哪种分配方式由java堆是否规整决定 ,而java堆是否规整又由所采用的垃圾回收器是否带有压缩整理功能 。比如向Serial、PerNew带有整理过程的收集器,采用指针碰撞算法。而向CMS收集器时,采用空闲列表。
④内存分配完成后,给分配的内存空间都初始为零值。
⑤然后设置对象的对象头信息。例如:对象的GC年龄,对象的哈希码等
⑥执行完new指令后,接着执行方法,按照程序员的设计进行初始化(有字节码中是否跟随invokespecial指令所决定),这样一个真正可用的对象才算完全产生出来)
过程:
类加载的过程包括了者五个阶段:加载、验证、准备、解析、初始化。这几个阶段指的是按顺序开始,并不是按顺序执行完成,通常在一个阶段执行的过程中会调用或激活另一个阶段。
在这里插入图片描述4.Java对象存在哪里?
堆,极少部分栈上分配,运行时常量池

运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号的引用,这部分将在类加载后进入方法区的运行常量池中存放。如果常量池无法在申请到内存时会抛出OutOfMemoryError异常
5.hashmap链表环问题;
多个线程并发扩容 时,在执行transfer()方法转移键值对时,造成链表成环,导致程序在执行get操作时形成死循环
当重新调整HashMap大小的时候,确实存在条件竞争,因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历如果条件竞争发生了,那么就死循环了
6.wait、sleep;
1、这两个方法来自不同的类分别是,sleep来自Thread类,是Thread类的静态方法,wait来自Object类。sleep是Thread的静态类方法,谁调用的谁去睡觉
2、sleep方法没有释放锁,而wait方法释放了锁
sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源
3、使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
4、sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
7.为什么要有interruptedException;说的是线程的
阻塞方法收到中断请求的时候 就会抛出InterruptedException异常。
什么是阻塞?等待阻塞、同步阻塞、其他阻塞
什么是中断?通俗解释就是杀毒软件正在全盘查杀病毒,此时我们不想让他杀毒,这时候点击取消,那么就是正在中断一个运行的线程。
每一个线程都有一个boolean类型的标志,此标志意思是当前的请求是否请求中断,默认为false。当一个线程A调用了线程B的interrupt方法时,那么线程B的是否请求的中断标志变为true
阻塞方法:如果线程B调用了阻塞方法,如果是否请求中断标志变为了true,那么它会抛出InterruptedException异常。抛出异常的同时它会将线程B的是否请求中断标志置为false
8.java采用什么字符编码;
unicode
9.char占多大,能放中文字符么;
2个,能。
unicode编码字符集中包含了汉字, 所以,char型变量中当然可以存储汉字啦。unicode编码占用两个字节,所以,char类型的变量也是占用两个字节。
10.进程线程协程的区别;
协程是一种用户态的轻量级线程,协程的调度完全由用户控制 。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存 的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
协程多与线程进行比较

  1. 一个线程可以有多个协程,一个进程也可以单独拥有多个协程、。
  2. 线程进程都是同步机制,而协程则是异步
  3. 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态
    11.cms和g1的区别,g1结构
    CMS:以获取最短回收停顿时间为目标的收集器,基于并发“标记清理”实现。
    1、初始标记:独占PUC(STW),仅标记GCroots能直接关联 的对象
    2、并发标记:可以和用户线程并行执行,标记所有可达对象
    3、重新标记:独占CPU(STW),对并发标记阶段用户线程运行产生的垃圾对象进行标记修正
    4、并发清理:可以和用户线程并行执行,清理垃圾

优点:并发,低停顿
缺点:对CPU非常敏感、无法处理浮动垃圾、CMS使用“标记-清理”法会产生大量的空间碎片
CMS 出现FullGC的原因:
1、年轻带晋升到老年带没有足够的连续空间,很有可能是内存碎片导致的
2、在并发过程中JVM觉得在并发过程结束之前堆就会满,需要提前触发FullGC

G1:是一款面向服务端应用 的垃圾收集器
特点:
1、并行与并发:G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短stop-The-World停顿时间。部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。
2、分代收集:分代概念在G1中依然得以保留。虽然G1可以不需要其它收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。也就是说G1可以自己管理新生代和老年代了。
3、空间整合:由于G1使用了独立区域(Region)概念,G1从整体来看是基于“标记-整理”算法实现收集,从局部(两个Region)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片。
4、可预测 的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点 ,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒

与其它收集器相比,G1变化较大的是它将整个Java堆划分为多个大小相等的独立区域(Region) ,同时,为了避免全堆扫描,G1使用了Remembered Set来管理相关的对象引用信息。当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏了。
,G1收集器步骤:
1、初始标记:GC直接关联STW很短
2、并发标记:所有,耗时较长,与用户线程并发
3、最终标记:STW,多个CPU并行执行
4、筛选回收:STW
而最终标记阶段需要吧Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但可并行执行。最后筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划,这一过程同样是需要停顿线程的,但Sun公司透露这个阶段其实也可以做到并发,但考虑到停顿线程将大幅度提高收集效率,所以选择停顿。下图为G1收集器运行示意图:
在这里插入图片描述
jdk8默认垃圾回收算法:
默认使用 Parallel Scavenge(新生代)+ Serial Old(老年代)

12.方法区和直接内存什么时候会oom;
a、运行时常量池溢出,比如使用List保持对常量池引用,避免FullGC回收常量池,导致常量池OOM
b、方法区用于存放Class的相关信息,运行时当大量的类需要加载,把方法区填满后,就会发生OOM。
下面哪种情况会导致持久区jvm堆内存溢出():
A. 循环上万次的字符串处理
B. 在一段代码内申请上百M甚至上G的内存
C. 使用CGLib技术直接操作字节码运行,生成大量的动态类
D. 不断创建对象
解答:AC,,,B直接内存也就是堆外内存,D堆内存。
直接内存不是虚拟机规范中定义的内存区域,也不是虚拟机运行时数据区域的一部分。属于堆外内存,也就是本机内存的一部分。通过本地native方法分配内存本机直接内存的分配不会受到Java堆大小的限制,肯定还是会受到本机总内存
DirectMemory容量可通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆的最大值(-Xmx指定)一样。
13.如何查看java内存使用情况(jconsole、命令jmap、jstack等等)
jconsole可以以图表化的形式显示各种数据:类,堆,等等内存使用情况
jps获取线程号pid
jstack pid可以知道 java程序是如何崩溃和在程序何处发生问题。
jmap 可以从 core文件或进程中获得内存的具体匹配情况(具体还有一些形式)
14.线程池中的阻塞队列如果满了怎么办;
拒绝策略4444种
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
15.Arraylist扩容过程,如果频繁扩容会怎么样;
copy的过程,重新创建数组的过程都是耗费时间和内存资源的过程,肯定会影响性能。最好能预估一下容量,避免频繁copy扩容。
16.volatile使用场景
(读写并发、或可能产生指令重排序问题的地方)volatile可以禁止指令重排序
19.CopyOnWriteArraylist
CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy ,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁 ,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器
CopyOnWriteArray增删改均需要内部持有一个ReentrantLock lock = new ReentrantLock(),底层是用volatile 声明的数组 array。
缺点:
1.所以无法保证数据的实时性,只能保证结果一致性。
2.同时存在两个对象占用内存,如果对象占用内存比较大,会频繁触发Full GC和Young GC
3.注意尽量批量添加数据进入数组,减少扩容开销
优点:
并发性且安全
Vector是增删改查方法都加了synchronized,保证同步,但是每个方法执行的时候都要去获得锁,性能就会大大下降,而CopyOnWriteArrayList 只是在增删改上加锁ReentrantLock,但是读不加锁,在读方面的性能就好于Vector,CopyOnWriteArrayList支持读多写少的并发情况。
22.http状态码2345开头代表啥
1xx Informational(信息性状态码) 接受的请求正在处理
2xx Success(成功状态码) 请求正常处理完毕
3xx Redirection(重定向) 需要进行附加操作以完成请求
4xx Client error(客户端错误) 客户端请求出错,服务器无法处理请求
5xx Server Error(服务器错误) 服务器处理请求出错
23.软弱引用什么的(是否回收and何时回收有关)
强引用:强引用就是指在程序中普遍存在的,类似“Object obj = new Object()”,这类引用,只要引用存在,就算内存空间不足了,GC也永远不会回收掉被引用的对象,而是抛出OOM异常。
软引用:描述一些还有用但并非必要的对象。如果内存空间足够,GC就不会回收软引用关联的对象,如果内存空间不足了将要发生OOM异常时 ,GC将会把referent列入回收范围之中,进行第二次回收。如果回收了这些referent(第二次回收)后内存空间还不足够,则抛出OOM异常。JDK1.2后,提供了SoftReference类来实现软引用。软引用可以和引用队列(ReferenceQueue)联合使用(SoftReference有构造函数可以传入ReferenceQueue来监听GC对referent的处理)。软引用可以用来实现内存敏感的高速缓存(在软引用的referent中添加缓存数据,当内存足够时,就可以直接从内存中取数据,提高运行效率。当内存不足时缓存数据就会被GC回收掉)。
弱引用:也是用来描述非必要对象的,但是它的强度比软引用更弱 。更弱体现在:被弱引用关联的对象只能生存到下次GC之前,当发生GC时,会无条件的回收掉这部分内存,即:无论内存是否足够,弱引用关联的到的对象都会被回收掉 。JDK1.2后,提供了WeakReference类来实现弱引用,弱引用也可以和引用队列联合使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值