导图社区 【270】计算机科学思维之深度和广度
通过对计算机科学思维的纵向扩展和横向扩展,认识世界,认识自己,认识JAVA面向对象编程思想,设计原则和设计模式。深入CAS原理,类比原子性和数据库事务,解析JAVA虚拟机JVM,JMM,字节码与synchronize,内存屏障和volatile。
编辑于2019-10-02 11:24:22语言
递归语言
编程语言
高级语言
面向过程编程语言
C语言
面向对象编程语言
c++
java
程序
JVM
字节码
汇编语言
机器语言
JVM内存模型
程序计数器(PC)
程序计数器是一块很小的内存空间,用于记录下一条要运行的指令。每个线程都需要一个程序计数器,各个线程之中的计数器相互独立,是线程中私有的内存空间。 字节码解释器工作是就是通过改变这个计数器的值来选取下一条需要执行指令的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器完成
指向虚拟机字节码指令的位置
无内存溢出(oom)异常
java虚拟机栈
java虚拟机栈也是线程私有的内存空间,它和java线程同一时间创建,保存了局部变量、部分结果,并参与方法的调用和返回
栈帧
每个方法在执行时都会床创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。
局部变量表
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序被编译成Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需要分配的最大局部变量表的容量。 局部变量表的容量以变量槽(Slot)为最小单位,32位虚拟机中一个Slot可以存放一个32位以内的数据类型(boolean、byte、char、short、int、float、reference和returnAddress八种)
操作数栈
Java虚拟机的解释执行引擎被称为"基于栈的执行引擎",其中所指的栈就是指-操作数栈。操作数栈也常被称为操作栈。 和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。 虚拟机在操作数栈中存储数据的方式和在局部变量区中是一样的:如int、long、float、double、reference和returnType的存储。对于byte、short以及char类型的值在压入到操作数栈之前,也会被转换为int。 虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。比如,iadd指令就要从操作数栈中弹出两个整数,执行加法运算,其结果又压回到操作数栈中
动态链接
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)
方法出口
当一个方法开始执行后,只有两种方式可以退出,一种是遇到方法返回的字节码指令;一种是遇见异常,并且这个异常没有在方法体内得到处理
其他
本地方法栈
本地方法栈和java虚拟机栈的功能相似,区别于 Java 虚拟机栈的是,Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。也会有 StackOverflowError 和 OutOfMemoryError 异常。
线程私有
java堆
为所有创建的对象和数组分配内存空间,被JVM中所有的线程共享
新生代
GC是统计学测算出超过98%以上的对象是一次就会被minor gc时回收的。但是我们不能较真的只给 他们留下2%,而是多算一些预留10%,用来存储新生代GC后存活的对象,所以我们GC其实是90%的控件,剩下的10%放在S1区域,然后结束后S1需要放回给S0区,也就是对调,既然能对调其实就是两个区域一般大,(其实对调就是复制算法)。这也是为什么会再有个10%的S0区域出来。这样比例就是8:1:1了(80%:s1:s0=80%:10%:10%=8:1:1)这里的8 和其中的一个 1 合起来供占据90%,GC就是清理的他们,始终保持着其中一个 1 区是空留的,保证GC的时候复制回收的对象有个存储的地方。
伊甸区(Eden space)
幸存区(Survivor 0 space)
幸存区(Survivor 1 space)
老年代
方法区/持久代(JDK1.8前)
也被称为永久区,与堆空间相似,被JVM中所有的线程共享。方法区主要保存的信息是类的元数据,方法区中最为重要的是类的类型信息、常量池、域信息、方法信息,其中运行时常量池就在方法区,对永久区的GC回收,一是GC对永久区常量池的回收;二是永久区对元数据的回收
运行时常量池
线程共享
元空间/Metaspace/Native memory(JDK1.8后)
充分利用了Java语言规范中的好处:类及相关的元数据的生命周期与类加载器的一致。 每个加载器有专门的存储空间 只进行线性分配 不会单独回收某个类 省掉了GC扫描及压缩的时间 元空间里的对象的位置是固定的 如果GC发现某个类加载器不再存活了,会把相关的空间整个回收掉
Klass Metaspace
NoKlass Metaspace
设计模式
创建型
工厂模式与抽象工厂模式 (Factory Pattern)
简单工厂模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类 使用参数或者配置文件等事先定义好的变量,然后利用分支判断初始化具体产品类并返回;不符合“开发-封闭”原则,每次增加产品,都需要修改类方法。工厂类单一,不用维护大量的工厂类;
一个工厂类根据传入的参量决定创建出那一种产品类的实例
抽象工厂模式
为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类 区别在于产品,如果产品单一,最合适用工厂模式,但是如果有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。再通俗深化理解下:工厂模式针对的是一个产品等级结构 ,抽象工厂模式针对的是面向多个产品等级结构的。
创建相关或依赖对象的家族,而无需明确指定具体类
工厂方法模式
定义一个创建对象的接口,让子类决定实例化那个类
单例模式 (Singleton Pattern)
某个类只能有一个实例,提供一个全局的访问点
建造者模式 (Builder Pattern)
封装一个复杂对象的构建过程,并可以按步骤构造
原型模式 (Prototype Pattern)
通过复制现有的实例来创建新的实例
结构型
适配器模式 (Adapter Pattern)
将一个类的方法接口转换成客户希望的另外一个接口
组合模式 (Composite Pattern)
将对象组合成树形结构以表示“”部分-整体“”的层次结构
装饰器模式 (Decorator Pattern)
动态的给对象添加新的功能
代理模式 (Proxy Pattern)
为其他对象提供一个代理以便控制这个对象的访问
享元模式 (Flyweight Pattern)
通过共享技术来有效的支持大量细粒度的对象
桥接模式 (Bridge Pattern)
将抽象部分和它的实现部分分离,使它们都可以独立的变化
外观模式 (Facade Pattern)
对外提供一个统一的方法,来访问子系统中的一群接口
过滤器模式 (Filter、Criteria Pattern)
使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来
制定不同的规则来对一组对象进行过滤,然后对过滤结果进行分组
行为型
迭代器模式(Iterator Pattern)
一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构
观察者模式(Observer Pattern)
对象间的一对多的依赖关系
模板模式(Template Pattern)
定义一个算法结构,而将一些步骤延迟到子类实现
命令模式(Command Pattern)
将命令请求封装为一个对象,使得可以用不同的请求来进行参数化
解释器模式(Interpreter Pattern)
给定一个语言,定义它的文法的一种表示,并定义一个解释器
中介者模式(Mediator Pattern)
用一个中介对象来封装一系列的对象交互
策略模式(Strategy Pattern)
定义一系列算法,把他们封装起来,并且使它们可以相互替换
状态模式(State Pattern)
允许一个对象在其对象内部状态改变时改变它的行为
备忘录模式(Memento Pattern)
在不破坏封装的前提下,保持对象的内部状态
责任链模式(Chain of Responsibility Pattern)
将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会
空对象模式(Null Object Pattern)
通过实现一个默认的无意义对象来避免null值出现
去掉对象null控制判断
面向对象设计原则
设计目标
开闭原则
软件实体(模块,类,方法等)应该对扩展开放,对修改关闭
里氏代换原则
所有引用基类的地方必须能透明地使用其派生类的对象
迪米特原则
最少知道原则
降低类之间的耦合
设计方法
单一职责原则
永远不要让一个类存在多个改变的理由
接口分隔原则
不能强迫用户去依赖那些他们不使用的接口
依赖倒置原则
高层模块不应该依赖于低层模块,二者都应该依赖于抽象
抽象不应该依赖于细节,细节应该依赖于抽象
针对接口编程,不要针对实现编程
组合/聚合复用原则
尽量使用组合/聚合,不要使用类继承
发展
指令固定
计算器
指令可存储可改变
计算机
编译性语言
解释性语言
范式
自然观/世界观——观念范式
逻辑体系——规则范式
编程范式
基于图灵机的命令编程范式
基于λ运算的函数编程范式
基于一般递归函数的逻辑编程范式
人的思路——心智模型
面向对象编程范式
柏拉图(Plato)原则:类的世界独立存在,对象世界由类创建而来
里氏替换原则(LSP):子类型(必须)能够替代其父类型
Parnas原则:接口与实现的分离,用户仅需要了解接口
心理认知因素——心理范式
宇宙
大统一理论
基本粒子
上帝粒子
引力子
《时间简史》
多重宇宙
人
认识自己
《幸福心理学》
人与自然
《蓝色星球》
《从太空看地球》
人与社会
《社会心理学》
计算机
计算机系统
硬件系统
软件系统
系统软件
操作系统
应用软件
图灵机
丘奇-图灵论
数学
语言
思想
大脑
人
宇宙
JNI
JNI是Java本机接口(Java Native Interface),是一个本机编程接口,它是Java软件开发工具箱(Java Software Development Kit,SDK)的一部分。JNI允许Java代码使用以其他语言编写的代码和代码库。Invocation API(JNI的一部分)可以用来将Java虚拟机(JVM)嵌入到本机应用程序中,从而允许程序员从本机代码内部调用Java代码。不过,对Java外部的调用通常不能移植到其他平台,在applet中还可能引发安全异常。实现本地代码将使您的Java应用程序无法通过100%纯Java测试。
native
native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
rt.jar sun.misc.Unsafe
原语
原语(primitive or atomic action)属于操作系统或计算机网络用语,是由若干条指令组成的,用于完成一定功能的一个过程。 原语是由若干个机器指令构成的完成某种特定功能的一段程序,具有不可分割性。即原语的执行必须是连续的,在执行过程中不允许被中断。
CAS
Compare And Swap比较并交换,线程修改主内存共享变量值前,先和原先拷贝的未操作前的变量值比较,若是相等(其他线程没动过)则替换,否则重新拷贝值再次计算。 CAS是一条CPU并发原语,调用sun.misc.Unsafe类 CAS比synchronized效率要好, 因为CAS是c语言实现的cpu锁机制, synchronized是Java锁。 缺点:可能产时间失败,循环时间长,CPU开销大 只能保证一个共享变量的原子操作 引出ABA问题
ABA
狸猫换太子 只管头尾相同,但中间过程可能有变动
规避ABA问题
原子引用
AtomicReference<V>
时间戳原子引用
增加修改版本号(时间戳) public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { Pair<V> current = pair; return expectedReference == current.reference && expectedStamp == current.stamp && ((newReference == current.reference && newStamp == current.stamp) || casPair(current, Pair.of(newReference, newStamp))); }
spring事务
数据库事务
事务(Transaction)是多个操作数据库的步骤(CRUD)的集合,是并发控制的单位,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。达到保持数据完整性的作用。
原子性
一个事务所有对数据库操作是一个最小单位,不可细分;要么执行,要么不执行
隔离性
事务之间可以同时执行,不会互相干扰,是隔离的
一致性
事务执行成功数据库变更,事务执行失败数据库不变更,即事务一致性
持久性
事务执行成功,之后的结果是持久的,一直保持
事务隔离级别
DCL版单例模式
双端检索机制
禁止指令重排版
public class VolatileDclDemo { //private static VolatileDclDemo instance; private static volatile VolatileDclDemo instance; public VolatileDclDemo() { System.out.println(Thread.currentThread().getName() + "\t VolatileDclDemo构造方法VolatileDclDemo()"); } //DCL(Dooble Check Lock 双端检索机制) public VolatileDclDemo getInstance(){ if (instance == null){ synchronized (VolatileDclDemo.class){ if (instance == null){ instance = new VolatileDclDemo(); } } } return instance; } }
synchronized
大脑
神经系统
大脑神经网络
神经网络模型
类脑芯片
云脑
《计算机与人脑》
人脑
高并发
高并行
低逻辑深度
下棋时很难走一步想百步
计算机
低并发
时间段内有很多的线程或进程在执行,但何时间点上都只有一个在执行,多个线程或进程争抢时间片轮流执行
特低并行
时间段和时间点上都有多个线程或进程在执行
N核cpu可以有N个并行
高逻辑深度
计算过程中需要进行的初等计算的数目
java.util.concurrent
atomic
重排
数据依赖性
编译器优化的重排
指令并行的重排
内存系统的重排
volatile
保证可见性
不保证原子性
禁止指令重排
幻读
不可重复读
脏读
内存屏障
StoreStore
确保Store1的数据在Store2以及后续Store指令操作相关数据之前对其它处理器可见(例如向主存刷新数据)。通常情况下,如果处理器不能保证从写缓冲或/和缓存向其它处理器和主存中按顺序刷新数据,那么它需要使用StoreStore屏障。
LoadLoad
确保Load1所要读入的数据能够在被Load2和后续的load指令访问前读入。通常能执行预加载指令或/和支持乱序处理的处理器中需要显式声明Loadload屏障,因为在这些处理器中正在等待的加载指令能够绕过正在等待存储的指令。 而对于总是能保证处理顺序的处理器上,设置该屏障相当于无操作。
LoadStore
确保Load1的数据在Store2和后续Store指令被刷新之前读取。在等待Store指令可以越过loads指令的乱序处理器上需要使用LoadStore屏障。
StoreLoad
确保Store1的数据在被Load2和后续的Load指令读取之前对其他处理器可见。StoreLoad屏障可以防止一个后续的load指令 不正确的使用了Store1的数据,而不是另一个处理器在相同内存位置写入一个新数据。
数据库隔离级别
Read Uncommited读未提交
Read Committed读已提交
Repeatable Read可重复读
Serialization串行化
有序性
原子性
内存
主内存的某一共享变量操作不可中断
中断后会造成数据丢失
数据库
spring事务
可见性
内存
主内存的某一共享变量值发生改变,将改变后的值推送给所有该变量的副本
JMM
主内存
工作内存
数据结构
数据
数学对象
编程=数据结构+算法
数据结构
底层存储结构
数组
链表
高级数据结构
哈希表
B+树
算法
递归
非递归
输出
输入
控制器
存储器
运算器
面向对象编程思想-JAVA
抽象
过程抽象
设计原则
设计模式
数据抽象
封装
setter
getter
继承
模板
抽象类
行为
接口
多态
引用多态
父类引用可以指向本类对象,也可指向子类对象。引用多态的强大主要体现在调用属性、方法时,可以根据引用具体指向的对象去调用
重写
方法多态
子类中可以重写父类的方法,在调用方法时根据引用指向的子类对象决定调用哪个具体的方法。方法多态的强大主要体现在可以根据调用时参数的不同,而自主匹配调用的方法
重载