并行和并发
1.并行跟并发有什么区别?
从操作系统的角度来看,线程是CPU分配的最小单位。
并行就是同一时刻,两个线程都在执行。这就要求有两个CPU去分别执行两个线程。
并发就是同一时刻,只有一个执行,但是一个时间段内,两个线程都执行了。并发的实现依赖于CPU切换线程,因为切换的时间特别短,所以基本对于用户是无感知的。
就好像我们去食堂打饭,并行就是我们在多个窗口排队,几个阿姨同时打菜;并发就是我们挤在一个窗口,阿姨给这个打一勺,又手忙脚乱地给那个打一勺。
进程和线程
进程:进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。我所理解的就是,一个服务就是一个进程. 比如:双击QQ,启动QQ,然后QQ把各种资源加载进内存, 这时,一个进程就诞生了.
线程:线程是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源。
操作系统在分配资源时是把资源分配给进程的, 但是 CPU 资源比较特殊,它是被分配到线程的,因为真正要占用CPU运行的是线程,所以也说线程是 CPU分配的基本单位。
比如在Java中,当我们启动 main 函数其实就启动了一个JVM进程,而 main 函数在的线程就是这个进程中的一个线程,也称主线程。
jvm内存
下面的图是上面的图的进一步解释
Java内存模型(JMM)
1.堆和栈都是 在Java语言虚拟机 层面上的.
栈内存线程私有的, 是什么意思呢, 可以理解为每个线程都有自己的一个栈内存, 它们之间互不可见互不影响.
堆内存是线程共享的.就是说每个线程都能共享堆内存的资源.
比如,堆内存有个int age= 1,那么a线程先把用到的堆内存的资源拷贝到自己的栈内存,就是 int age =1拷贝到自己的栈内存中,然后再让int age =2,再把int age =2放回线程共享的堆内存中, 当其他线程去读取age的值时就变成了int age =2;
2.Java内存模型(JMM)定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存
(Main Memory)中,每个线程都有一个私有的本地内存
(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。
堆内存 ,数组会把自己的实体对象放到堆内存, 把实体对象的地址存放到栈内存 图
类的定义 图
对象内存指向图
成员变量和局部变量
然后每个方法都有自己的栈帧,栈帧里包括局部变量表,操作数栈,动态链接,方法出口四个部分.
经过上面的图片知识学习,大致总结下栈内存,堆内存,和方法区都放哪些东西
栈内存 (线程私有) | 堆内存 (线程共享) (包含字符串常量池) | 方法区 (线程共享) (包含运行时常量池) | |
类 | 类new出的实例对象在堆内存的引用地址 | 放类new出来的实例对象 | 类的信息,运行时常量池.方法数据.方法代码 |
数组 | 引用地址 | 放数组new出来的实例对象 | |
集合 | 引用地址 | 放数组new出来的实例对象 | |
字符串 | 引用地址 | 字符串常量池存放字符串对象的引用地址, 字符串对象创建在堆内存内, | |
变量 | 局部变量 | 成员变量 | |
常量(除字符串外) | 引用地址 | 用final修饰的变量 | |
基本类型 int a = 3 | 引用地址 | 3 存在常量池中 |
上面的表格先等一下,先说一下方法区
什么是方法区
方法区,是JVM概念上的一个区,并不会随着JDK版本的变化而变化。
但是,方法区的具体实现,却会随着JDK版本的变化而变化的。
JDK7以前,方法区的实现是在“永久代”里;到了JDK8以后,则在Metadata空间里。
方法区的位置?
《Java虚拟机规范》中明确说明:尽管所有的方法区在逻辑上是属于堆的一部分,但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。但对于HotSpotJVM而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。
所以,方法区可以看作是一块独立于Java堆的内存空间.
方法区被线程共享吗?
是的,和堆内存一样,是被线程共享的
方法区都存什么?
java官方书上说:它储存每个类的结构,例如运行时常量池,字段和方法数据,以及方法和构造函数的代码,包括用于类和实例初始化以及接口初始化的特殊方法,
百度说:它用于存储已被虚拟机加载的类信息, 常量, 静态变量, 即时编译器编译后的代码等数据
理解后简化说: 存放类的信息.运行时常量池.方法数据.方法代码.
方法区的理解
- 方法区(Method Area)与Java堆一样,是各个线程共享的内存区域
- 多个线程同时加载统一个类时,只能有一个线程能加载该类,其他线程只能等等待该线程加载完毕,然后直接使用该类,即类只能加载一次。
- 方法区在JVM启动的时候被创建,并且它的实际物理内存空间和Java堆区一样都可以是不连续的。
- 方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展。
- 方法区是接口,元空间或者永久代是方法区的实现
- 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:
- java.lang.OutofMemoryError:PermGen space(JDK7之前)
- 或者java.lang.OutOfMemoryError:Metaspace(JDK8之后)
- 举例说明
- 1.方法区 OOM加载大量的第三方的jar包
- 2.Tomcat部署的工程过多(30~50个)
- 3.大量动态的生成反射类
关闭JVM就会释放这个区域的内存。