进程
是动态的,
程序的一次执行过程,
是系统运行程序的基本单位.
线程
负担比进程小
比进程更小的执行单位,
(进程和线程为)1对多的关系,
同类多个线程共享堆和方法区资源,
独立的程序计数器,虚拟机栈和本地方法栈.
程序计数器:
字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理
在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了
程序计数器私有主要是为了线程切换后能恢复到正确的执行位置。
虚拟机栈
虚拟机栈由一个个栈帧组成,每个栈帧都有局部变量表(存放编译器可知的各种基本数据类型和对象引用) 操作数栈,动态连接 方法出口
java内存可粗分为堆内存和栈内存(虚拟机栈中局部变量表)
为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地方法栈是线程私有的。
局部变量表
存放了编译器可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,
本地方法栈
虚拟机栈执行java方法,本地方法栈执行虚拟机用到的native方法服务
堆
所有线程共享的内存区域
目的 存放对象实例
垃圾收集器管理的主要区域(GC堆Garbage Collected Heap)
方法区
是各个线程共享的内存区域
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
运行时常量池
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P408pczN-1616414610609)(C:\Users\11813\AppData\Roaming\Typora\typora-user-images\image-20210318093347007.png)]
直接内存
直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 异常出现。
本机直接内存的分配不会收到 Java 堆的限制,但是,既然是内存就会受到本机总内存大小以及处理器寻址空间的限制。
java创建对象过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EBHQE6Vj-1616414610611)(C:\Users\11813\AppData\Roaming\Typora\typora-user-images\image-20210318093557064.png)]
1,类加载检查
虚拟机遇到new指令,检查指令的参数能否在常量池中定位到这个类的符号引用并检查这个类是否被加载 解析 初始化过,如果没有,则要执行类加载过程
2.分配内存
所需大小在类加载完成后即可确定,分配即为从java堆中划分出一块确定大小的空间.
分配方式:指针碰撞,空闲列表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fbKzpfvO-1616414610613)(C:\Users\11813\AppData\Roaming\Typora\typora-user-images\image-20210318094936649.png)]
分配方式:java堆是否规整
规整取决:垃圾收集器是否有带压缩整理功能
3.初始化零值
将拿到的内存空间全部初始化为0值
目的:保证java代码不赋初始值就可以使用(开始为0)
4.设置对象头
一些对象信息的设置*比如找到类的元数据信息,对象的哈希码,对象的gc分带年龄问题等
5执行init方法
按照程序员的意愿进行初始化
创建过程可能出现的问题
内存分配并发问题
解决办法:
1 cas+重试
cas是乐观锁的实现方式(每次不枷锁假设没有冲突,如果冲突则重试直到成功)
2 TLAB
预先分配一块内存,需要内存到这里来拿,耗尽则用cas加重试
并发和并行的区别
并发: 同一个时间段,多个任务都在执行(不一定同时执行)
并行: 单位时间内,多个任务同时执行.
为什么要使用多线程?
先从总体上来说:
- 从计算机底层来说: 线程可以比作是轻量级的进程,是程序执行的最小单位,线程间的切换和调度的成本远远小于进程。另外,多核 CPU 时代意味着多个线程可以同时运行,这减少了线程上下文切换的开销。
- 从当代互联网发展趋势来说: 现在的系统动不动就要求百万级甚至千万级的并发量,而多线程并发编程正是开发高并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能力以及性能。
再深入到计算机底层来探讨:
- 单核时代: 在单核时代多线程主要是为了提高 CPU 和 IO 设备的综合利用率。举个例子:当只有一个线程的时候会导致 CPU 计算时,IO 设备空闲;进行 IO 操作时,CPU 空闲。我们可以简单地说这两者的利用率目前都是 50%左右。但是当有两个线程的时候就不一样了,当一个线程执行 CPU 计算时,另外一个线程可以进行 IO 操作,这样两个的利用率就可以在理想情况下达到 100%了。
- 多核时代: 多核时代多线程主要是为了提高 CPU 利用率。举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,CPU 只会一个 CPU 核心被利用到,而创建多个线程就可以让多个 CPU 核心被利用到,这样就提高了 CPU 的利用率。
多线程可能遇到的问题
内存泄漏 死锁 线程不安全
线程死锁
多个线程同时被阻塞,全都等待某个资源被释放.
A持有1想要2,B持有2想要1
死锁条件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e8Ma4ZzH-1616414610615)(C:\Users\11813\AppData\Roaming\Typora\typora-user-images\image-20210318101910995.png)]
避免死锁
1,破坏请求与保持(一次性申请所有的资源)
2.破坏不剥夺条件(可以主动释放他人的资源)
3.破坏循环等待
线程的生命周期和状态
new:创建之后处于的状态
ready 调用start方法后开始运行的状态
runing 获得cpu时间片后的状态
Synchronized关键字
使用方式
1.修饰实例方法
作用于当前对象实例加锁,进入同步代码前要获得 当前对象实例的锁
2.修饰静态方法