自己做网站挂广告,建工网校官网app,南昌seo快速排名,申请建设网站的报告Java运行时的数据区域分布#xff1a;
一、共享区域#xff1a;
#xff08;1#xff09;方法区:存储了每个类的信息#xff08;包括类的名称、方法信息、字段信息#xff09;、静态变量、常量以及编译器编译后的代码等。其中常量池就是在此区域#xff1a;记录了每一…Java运行时的数据区域分布
一、共享区域
1方法区:存储了每个类的信息包括类的名称、方法信息、字段信息、静态变量、常量以及编译器编译后的代码等。其中常量池就是在此区域记录了每一个类或接口的常量池的运行时表示形式运行期间也可将新的常量放入运行时常量池中比如String的intern方法。
2堆jvm中最大的一片区域所有实例对象的内存分配都在这里进行划分。当对象无法在此得到分配空间时就会OutOfMemory。这部分空间也是Java垃圾收集器管理的主要区域。
二、线程私有
1程序计数器在JVM中多线程是通过线程轮流切换来获得CPU执行时间的因此在任一具体时刻一个CPU的内核只会执行一条线程中的指令因此为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置每个线程都需要有自己独立的程序计数器并且不能互相被干扰否则就会影响到程序的正常执行次序。因此可以这么说程序计数器是每个线程所私有的。同时如果线程数量太多对于cpu来说就需要频繁切换线程导致cpu占有率上升。
2java栈存放的是一个个的栈帧每个栈帧对应一个被调用的方法在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池运行时常量池的概念在方法区部分会谈到的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。
当线程执行一个方法时就会随之创建一个对应的栈帧并将建立的栈帧压栈。当方法执行完毕之后便会将栈帧出栈。因此可知线程当前执行的方法所对应的栈帧必定位于Java栈的顶部。当程序设计时调用的栈的深度太深有可能会导致无法创建栈帧导致OutOfMermory。
3本地栈类似Java栈但存放但是native方法的栈帧。
线程/工作内存/主内存 三者的关系
一、每个线程都有一个独立的工作内存用于存储线程私有的数据
二、Java内存模型中规定所有变量都存储在主内存主内存是共享内存区域所有线程都可以访问
三、线程对变量的操作(读取赋值等)必须在工作内存中进行。线程安全问题的根本原因
1首先要将变量从主内存拷贝的自己的工作内存空间
2然后对变量进行操作操作完成后再将变量写回主内存
3不能直接操作主内存中的变量工作内存中存储着主内存中的变量副本拷贝
4因此不同的线程间无法访问对方的工作内存线程间的通信(传值)必须通过主内存来完成。
主内存和工作内存
一、主内存是在运行期间所有变量的存放区域当工作内存是运行期间中某一线程独立私有的内存存放区域
二、线程间无法访问对方的工作内存空间都是通过主内存交换来实现
三、主内存的变量在工作内存中的值是复制过去的副本读写完成后刷新主内存这意味着主内存如果发生了改变工作内存并无法获得最新的结果
四、多个线程对一个共享变量进行修改时都是对自己工作内存的副本进行操作相互不可见。主内存最后得到的结果是不可预知的
new Thread(new Runnable() {Overridepublic void run() {System.out.println(thread1 is start);boolean wIsStop false;while (!isStop) {}System.out.println(thread1 is going to stop!);}}).start();上面这段代码中主线程改变isStop的变量后是无法退出循环的。因为isStop这个变量在线程中从主内存读取到副本后一直使用的是工作内存的副本。
Synchronized和Volatile
由于线程运行时对工作内存的变量进行操作时都是副本。 产生了两个问题 1.如何使工作内存副本从主内存获取最新的值 2.如何使其副本是最新避免在写入之前被其他线程修改
在主线程中改变了isStoptrue后 下面这段代码可以退出循环
new Thread(new Runnable() {Overridepublic void run() {System.out.println(thread2 is start);while (!isStop) {synchronized(String.class) {}}System.out.println(thread2 is going to stop!);System.out.println(break count: count);}}).start();或者是在while循环中加入了System.out.println的语句也会退出循环。
new Thread(new Runnable() {Overridepublic void run() {System.out.println(thread2 is start);while (!isStop) {System.out.println(thread2 stopFlag isStop);}System.out.println(thread2 is going to stop!);System.out.println(break count: count);}}).start();其实System.out.println的实现中使用了Synchonized关键字获取输出对象的锁。 一开始以为是Synchronized引起了工作内存从主内存刷新最新的数据感觉不太合理后台各种搜索后找到比较靠谱的答案 jvm有一个锁优化原则那就是 粗化锁。如果一系列的连续操作都对同一个对象反复加锁和解锁甚至加锁操作是出现在循环体中的那即 使没有线程竞争频繁地进行互斥同步操作也会导致不必要的性能损耗。 如果虚拟机探测到有这样一串零碎的操作都对同一个对象加锁将会把加锁同步的范围扩展膨胀到整个操作序列的外部由多次加锁编程只加锁一次。 ------更新-------- 上面粗化锁的解释应该也不准确锁的对象并不是变量isStop并不能将isStop加上一个线程可见的属性。 在synchronized锁操作中由于是一个耗时操作jvm会尽力在cpu空闲时间去主内存同步工作内存中变量的值。 ------更新--------
同样下面的代码也能退出循环
new Thread(new Runnable() {Overridepublic void run() {System.out.println(thread3 is start);while (!isStop) {try {Thread.sleep(1);}catch (Exception e) {e.printStackTrace();}}System.out.println(thread3 is going to stop!);}}).start();发现这个循环体里面并没有synchronized的关键字猜测只能是jvm在线程有空闲的时间尽力的去主存取最新数据。
引入Synchronized后同步代码块在循环体内synchronized保证原子性和有序性可见性。
为了保证不同线程间可以获得同一个共享变量Volatile也可以做到。
volatile变量在每次被线程访问时都强迫从主内存中读该变量的值而当该变量发生变化时又会强迫将最新的值刷新到主内存任何时刻不同的线程总是能够看到该变量的最新值。 上面代码中对isStop用volatile修饰也可以退出线程。