JVM多线程并发编程

此文仅做学习笔记记录

对内存管理以及线程没有系统的学习,工作中很多时候出现问题都不能快速的解决,现对这部分知识做一个整理。

JVM

以下对JVM运行时的情况做一些整理

JVM运行数据是数据区会分为线程共享部分和线程独占部分,而线程共享部分中包括:方法区,堆内存;线程独占部分分为:虚拟机栈,本地方法栈,程序计数器。
在这里插入图片描述

  1. 线程共享:所有线程都能访问这块内存数据,随虚拟机或者GC而创建和销毁;
    -方法区:用来储存加载的类信息、常量、静态变量、编译后的代码等数据;虚拟机规范中这是一个逻辑区 域。具体实现根据不同虚拟机来实现。如:oracle在hotspot在java7中方法区放在永久代,java8放在元数据空间,并且通过GC机制对这个区域进行管理
    -堆内存:可以分为老年代、新生代(Eden、From Survivor、To Survivor) JVM启动时创建,存放对象的实例。垃圾回收器主要就是管理堆内存。
    如果堆内存满了,就会出现OOM。
    当对象被创建的时候回放到Eden里面,当Eden中对象太多会进行一次Minor GC,把Eden中的对象放到Survivor区,当没熬过一次Minor GC 年龄就会增加一岁,当到达一定的岁数就会被移动到老年代里面,每次GC都会保证To Survivor中为空,把存活的对象按照年龄放到老年代或者From Survivor中去。

疑问:什么是老年代、新生代或者说是年轻代、持久代??
年轻代:
所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分为三个区。一个Eden区,两个Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活将被复制到另外一个Survivor区,当这个Survivor区也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来的对象和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor区过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。

从以上表述可以看出来,新生对象在年轻代(Eden)中诞生,当触发GC的时候,会把存活的对象复制到年轻代(Survivor)中,当其中一个Survivor区满了的时候,会被复制到另外一个Survivor区,那么当两个Survivor区都满了,会把经过N次(周期较长)GC周期的对象复制到老年代中。
老年代:
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
持久代:
用于存放静态文件,如java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久空间来存放这些运行过程中新增的类。持久代大小通过 -XX:MaxPermSize = 进行设置。

疑问:那么什么时候会触发GC呢?GC有几种类型?
由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC 和 Full GC
这两种类可以理解为,一个是日常打扫,一个是大扫除
Scavenge GC
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden区能尽快空闲出来。
Full GC
对整个堆进行整理,包括Young、Tenured 和 Perm。Full GC 因为需要对整个堆进行回收,所以比 Scavenge GC 要慢,因此应该尽可能减少 Full GC 的次数。在对JVM调优的过程中,很大一部分工作就是对于 Full GC 的调节。
所以在JVM调优的过程中,尽可能减少大扫除(Full GC)的次数。
(该如何设置,后续更新。)

  1. 线程独占:每个线程都会有它独立的空间,随线程生命周而创建和销毁
    -虚拟机栈:每个线程都在这个空间有一个私有的空间。线程栈由多个栈帧(Stack Frame)组成。一个线程会执行一个活多个方法,一个方法对应一个栈帧。
    栈帧内存包含:局部变量表、操作数栈、动态链接、方法返回地址、附加信息等。栈内存默认最大是1M,超出会跑出StackOverFlowError(运行代码逻辑)。
    -本地方法栈:和虚拟机栈功能类似,虚拟机栈是为虚拟机执行JAVA方法而准备的,本地方法栈是为虚拟机使用Native本地方法而准备,超出大小也会抛出StackOverFlowError。
    -程序计数器:(Progrem Counter Register)记录前线程执行字节码的位置,存储的字节码指令地址,如果执行Native方法,则计数器值为空。
    每个线程都在这个空间有一个私有空间,占用内存空间很少,CPU在同一时间,会执行一条线程中的指令,JVM多线程会轮流切换并分配CPU;
    执行时间的方式。为了线程切换后,需要通过程序计数器来恢复到正确的位置(用来记录线程的执行位置)。

线程状态

在这里插入图片描述
6个状态定义:java.lang.Thread.State
1.New:尚未启动的线程的线程状态。
2.Runnable:可运行线程的线程状态,等待CPU调度。
3.Blockd:线程阻塞等待监视器锁定的线程状态。(处于Synchronize同步代码块活方法中阻塞)
4.Waiting:等待线程的线程状态。不带超时的方式:Object.wait、Thread.join、LockSupport.park
5.Timed Waiting:具有指定等待时间的等待线程的线程状态,带有超时的方式:Thread.sleep、Object.wait、Thread.join、LockSupport.parkNanos、LockSupport.parkUntil

线程中止

不正确的线程中止-stop
stop:中止线程,并且清楚监控器锁的信息,但是可能导致线程安全问题;

正确的线程中止-interrupt
如果目标线程在调用Object class的wait()、wait(long)或wait(long,int)方法、join()、join(long,int)或sleep(long,int)方法是阻塞,那么Interrupt会生效,改线程的中断状态
将被清除,跑出InterruptedException异常。
如果目标线程是被I/O 或者NIO中的Channel所阻塞,同样 I/O操作会被中断或者返回特殊异常值。达到中止线程的目的。
如果以上条件都不满足,则会设置此线程的中断状态。

可以看下以下例子:

public static int i = 0, j = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            ++i;
            try {
                Thread.sleep(10000L);//模拟操作
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.err.println("线程停止");
            }
            ++j;
        });
        thread.start();
        //确保i自增成功
        Thread.sleep(1000L);
//        thread.stop();
        thread.interrupt();
        while (thread.isAlive()){
            //确保线程已经中止
        }
        System.out.println("i="+i+",j="+j);
    }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值