3. JVM、并发

4 篇文章 0 订阅
2 篇文章 0 订阅

JVM

1. 方法区与常量池

JDK1.8中字符串常量池运行时常量池逻辑上属于方法区,但是实际存放在堆内存中

2. 静态变量不入栈

3. 什么是JVM内存模型?

Java 内存模型(下文简称 JMM)就是在底层处理器内存模型的基础上,定义自己的多线程语义。
它明确指定了一组排序规则,来保证线程间的可见性。
这一组规则被称为 Happens-Before

怎么理解 happens-before 呢?
happens-before 也是为了保证可见性,比如那个解锁和加锁的动作,可以这样理解,线程1释放锁退出同步块,线程2加锁进入同步块,那么线程2就能看见线程1对共享对象修改的结果。

Java 提供了几种语言结构,包括 volatile, final 和 synchronized, 它们旨在帮助程序员向编译器描述程序的并发要求,其中:

  • volatile - 保证可见性和有序性
  • synchronized - 保证可见性和有序性; 通过管程(Monitor)保证一组动作的原子性
  • final - 通过禁止在构造函数初始化和给 final 字段赋值这两个动作的重排序,保证可见性

编译器在遇到这些关键字时,会插入相应的内存屏障,保证语义的正确性。

总的来说,Java 内存模型描述的是多线程对共享内存修改后彼此之间的可见性。
另外,还确保正确同步的Java 代码可以在不同体系结构的处理器上正确运行。

4. 栈和栈帧

每个方法在执行的时候就会创建一个栈帧,它包含局部变量表、操作数栈、动态链接、方法出口等信息,局部变量表又包括基本数据类型和对象的引用;

5. JVM 中的常量池

JVM常量池主要分为Class文件常量池、运行时常量池,全局字符串常量池,以及基本类型包装类对象常量池。

6. 如何判断一个对象是否存活?

判断一个对象是否存活,分为两种算法
1:引用计数法;
2:可达性分析算法;

从一个被称为GC Roots的对象向下搜索,如果一个对象到GC Roots没有任何引用链相连接时,说明此对
象不可用,在java中可以作为GC Roots的对象有以下几种:

  • 虚拟机栈中引用的对象
  • 方法区类静态属性引用的变量
  • 方法区常量池引用的对象
  • 本地方法栈JNI引用的对象

7. 强引用、软引用、弱引用、虚引用是什么,有什么区别?

  • 强引用,就是普通的对象引用关系,如 String s = new String(“ConstXiong”)
  • 软引用,用于维护一些可有可无的对象。只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。SoftReference 实现
  • 弱引用,相比软引用来说,要更加无用一些,它拥有更短的生命周期,当 JVM 进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。WeakReference 实现
  • 虚引用是一种形同虚设的引用,在现实场景中用的不是很多,它主要用来跟踪对象被垃圾回收的活动。PhantomReference 实现

8. 堆内存划分

在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old ),新生代默认占总空间的1/3,老年代默认占 2/3。
新生代有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1。

9. 元空间

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:

-XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。

-XX:MaxMetaspaceSize,最大空间,默认是没有限制的。

除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:

-XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集.

-XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集

某种程度上说,该图也是正确的:
在这里插入图片描述




并发

1. 线程和进程

根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。

2. Runnable和Callable

Call方法可以抛出异常,run方法不可以。

3. 线程状态

Java线程具有五种基本状态:

  • 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();

  • 就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于
    就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;

  • 运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。
    注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

  • 阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。

    • 根据阻塞产生的原因不同,阻塞状态又可以分为三种:
    • 1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
    • 2.同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
    • 3.其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
  • 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

4. 死锁的4个条件

  • 互斥: 同一时刻该资源只能由一个线程占用。
  • 请求与保持: 一个线程请求获取资源被阻塞,另一个线程对已获得的资源保持不放。
  • 不剥夺: 线程在获取资源后,在未使用完之前不能被其他线程强行剥夺,只能自己使用完自己释放。
  • 循环等待: 若干线程之间形成循环等待资源关系。

5. 避免线程死锁

只要破坏产生死锁的四个条件中的其中一个就可以了

  • 互斥
    这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)
  • 请求与保持
    一次性申请所有资源
  • 不剥夺
    占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
  • 循环等待
    靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。
  • 锁排序法:(必须回答出来的点)
    • 指定获取锁的顺序,比如某个线程只有获得A锁和B锁,才能对某资源进行操作
    • 多线程:指定锁的获取顺序,比如规定,只有获得A锁的线程才有资格获取B锁,按顺序获取锁就可以避免死锁。这通常被认为是解决死锁很好的一种方法。

6. shutdown() VS shutdownNow()

  • shutdown() :关闭线程池,线程池的状态变为 SHUTDOWN。线程池不再接受新任务了,但是队列里的任务得执行完毕。
  • shutdownNow() :关闭线程池,线程的状态变为 STOP。线程池会终止当前正在运行的任务,并停止处理排队的任务并返回正在等待执行的 List。

7. start() 和 run()

调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。

8. Thread类中的yield方法

Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。

9. ReentrantLock 和 synchronized

  • 1.两者都是可重入锁
  • 2.synchronized 依赖于 JVM 而 ReentrantLock 依赖于 API(JDK层面,需要 lock() 和 unlock() 方法配合 try/finally)
  • 3.ReentrantLock 比 synchronized 增加了一些高级功能
    ①等待可中断;②可实现公平锁;③可实现选择性通知(锁可以绑定多个条件)
    • 等待可中断.通过lock.lockInterruptibly()来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
    • ReentrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。
      所谓的公平锁就是先等待的线程先获得锁。(先到先得)
      ReentrantLock默认情况是非公平的,可以通过 ReentrantLock类的ReentrantLock(boolean fair)构造方法来制定是否是公平的。
    • ReentrantLock类线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。 在使用 notify()/notifyAll() 方法进行通知时,被通知的线程是由 JVM 选择的,用ReentrantLock类结合Condition实例可以实现“选择性通知”。

10. synchronized 关键字和 volatile 关键字

  • volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字两者都能保证
  • volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized关键字解决的是多个线程之间访问资源的同步性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值