Java面试:Java 内存分配与回收策略及Java死锁
简述 java 内存分配与回收策略以及 Minor GC 和Major GC (full GC)
内存分配
栈区: 栈分为 java 虚拟机栈和本地方法栈
堆区: 堆被所有线程共享区域, 在虚拟机启动时创建, 唯一 目的存放对象实例 。堆 区是 gc 的主要区域, 通常情况下分为两个区块年轻代和年老代 。更细一点年轻代 又分为 Eden 区, 主要放新创建对象, From survivor 和 To survivor 保存 gc 后幸 存下的对象, 默认情况下各自占比 8:1:1。
方法区: 被所有线程共享区域, 用于存放已被虚拟机加载的类信息, 常量, 静态变 量等数据 。被 Java 虚拟机描述为堆的一个逻辑部分 。习惯是也叫它永久代 (permanment generation)
程序计数器: 当前线程所执行的行号指示器 。通过改变计数器的值来确定下一条指 令, 比如循环, 分支, 跳转, 异常处理, 线程恢复等都是依赖计数器来完成 。线程 私有的 。
回收策略以及 Minor GC 和 Major GC
● 对象优先在堆的 Eden 区分配
● 大对象直接进入老年代
● 长期存活的对象将直接进入老年代
当 Eden 区没有足够的空间进行分配时, 虚拟机会执行一次 Minor GC.Minor GC 通常 发生在新生代的 Eden 区, 在这个区的对象生存期短, 往往发生 GC 的频率较高, 回 收速度比较快;Full Gc/Major GC 发生在老年代, 一般情况下, 触发老年代 GC 的时候 不会触发 Minor GC,但是通过配置, 可以在 Full GC 之前进行一次 Minor GC 这样可以 加快老年代的回收速度 。
如何查看 java 死锁
private static final String lock1 = "lock1";
private static final String lock2 = "lock2";
public static void main(String[] args) {
Thread thread1 = new Thread( - {
while (true) {
synchronized (lock1) {
try {
System.out.println(Thread.currentThread.getName + lock1);
Thread.sleep(1000);
synchronized (lock2){
System.out.println(Thread.currentThread.getName +
lock2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
Thread thread2 = new Thread( - {
while (true) {
synchronized (lock2) {
try {
System.out.println(Thread.currentThread.getName + lock2);
Thread.sleep(1000);
synchronized (lock1){
System.out.println(Thread.currentThread.getName + lock1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
死锁代码演示
1. 程序运行, 进程没有停止。
2. 通过 jps 查看 java 进程, 找到没有停止的进程
3. 通过 jstack 9060 查看进程具体执行信息
Java 死锁如何避免
造成死锁的几个原因
(1) 一个资源每次只能被一个线程使用
(2) 一个线程在阻塞等待某个资源时, 不释放已占有资源
(3) 一个线程已经获得的资源, 在未使用完之前, 不能被强行剥夺
(4) 若干线程形成头尾相接的循环等待资源关系
这是造成死锁必须要达到的 4 个条件, 如果要避免死锁, 只需要不满足其中某一个条 件即可 。而其中前 3 个条件是作为锁要符合的条件, 所以要避免死锁就需要打破第 4 个条件, 不出现循环等待锁的关系 。
在开发过程中
1.要注意加锁顺序, 保证每个线程按同样的顺序进行加锁
2.要注意加锁时限, 可以针对锁设置一个超时时间
3.要注意死锁检查, 这是一种预防机制, 确保在第一时间发现死锁并进行解决
我来说两句