4 JVM
Java Virtual Machine: 翻译工作,可以跨平台。狭义上指的就 HotSpot(JVM有很多版本,但是使用最多的是HotSpot)
- jvm识别.class后缀文件(翻译,系统不能识别.class文件)
- 解析文件中指令 (翻译)
- 调用操作系统上的函数(windows、linux、macos)
- 注意:JVM只识别字节码,与语言无关,解耦。(Java、Groovy 、Kotlin、Jruby等等语言都可以编译成字节码)
释义
- **流程:**java程序(javac编译)-----java字节码-----JVM(Java类加载器->运行时数据区->执行引擎)-------操作系统函数(linux/windows/macos)
- JRE(Java Runtime Environment):运行时环境,包含JVM,提供了其他类库(比如操作文件,连接网络,使用I/O等)。
- JDK:供了一些小工具,比如 javac(编译代码)、java、jar (打包代码)、javap(反编译<反汇编>)等
运行时数据区:自动内存管理机制
-
线程私有
-
虚拟机栈:存储当前线程运行方法所需的数据,指令、返回地址。虚拟机栈是基于线程,生命周期跟随线程。栈里的每条数据,就是栈帧。
栈帧/大小限制(- Xss)
-
本地方法栈: 虚拟机栈用于管理 Java 函数的调用,而本地方法栈则用于管理本地方法的调用(由 C 语言实现)。HotSpot直接把本地方法栈和虚拟机栈合二为一
-
程序计数器 :较小的内存空间,JVM中唯一不会OOM(OutOfMemory)的内存区域。主要用来记录各个线程执行的字节码的地址,例如,分支、循环、跳转、异常、线程恢复等都依赖于计数器。(作用:时间片轮转应用。记录执行到哪一行标志)
-
-
线程共享
-
方法区/永久代 :存放已被虚拟机加载的类相关信息,包括类信息、静态变量、常量、运行时常量池、字符串常量池
-
堆 :JVM 上最大的内存区域,申请的几乎所有对象,都是在这里存储。垃圾回收,操作的对象就是堆。需要不定期回收, GC(Garbage Collection
-
java堆的大小参数设置 (例如- Xmx256m)
- -Xms:堆的最小值;
- -Xmx:堆的最大值;
- -Xmn:新生代的大小;
- -XX:NewSize;新生代最小值;
- -XX:MaxNewSize:新生代最大值;
Java平台,标准版工具参考:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
栈帧:都包含四个区域
- 局部变量表:存放局部变量的,Java的八大基础数据类型、Object对象(存放其引用地址)
- 操作数栈:方法执行的操作数,操作的的元素可以是任意的java数据类型
- 动态连接:Java语言特性多态
- 返回地址:正常返回(调用程序计数器中的地址作为返回)、异常的话(通过异常处理器表<非栈帧中的>来确定)
class类加载:加载、验证、准备、解析、初始化
java方法开始执行:入栈
java方法执行完毕:出栈
一个方法就是一个栈帧
javap命令行: javap -c (堆代码进行反汇编)
jps:查看进程 (JVMStack)
HSDB:HotSpot Debugger
内存划分:
- 指针碰撞: 内存规律
- 空闲列表:空位不规则
- 并发
- CAS失败重试
- TLAB 本地线程分配缓存,使用线程本地分配块
Object
- finalize() : 优先级低,需要等待; 只执行一次。 ===>try finally
可达性分析算法
对象引用
- 强引用:
- 软引用( SoftReference):oom时,才会回收。
- 弱引用(WeakReference):gc时,就会回收。 eg:threadLocal
- 虚引用:
栈跟随线程存在:线程结束,栈内存也结束(如方法栈,局部变量)
阈值
阀值
新生代: 对象朝生夕死
- 复制算法:高效,利用率只有一半
老年代:经过多次垃圾回收,依然存在。越来越难回收
- 标记清除算法:标记可回收/不可回收/未分配。 特点:效率不稳定, 内存碎片导致提前GC (内存不是连续的,分配内存时需要连续)
- 标记整理算法:在清除算法的基础上,对象移动,把空闲内存移在一起。(效率低,没有内存碎片)
JVM常见垃圾收集器:
- 单线程垃圾收集器:serial /serial Old
- 多线程并行垃圾收集器:ParNew/Paralel Scavenge/ Parallel Old
- 多线程并发来记收集器: CMS(清除)/ G1(整理)
CMS:concurrent mark sweep, 老年代,标记清除算法。
5.序列化
概念:将数据结构或对象转化为二进制串的过程
序列化方案:
- serializable (externalizable,类似parcelable)
- parcelable (android独有)
- json/xml/protbuf
serializable
- serialVersionUID作用:通常是个哈希码,用于对象版本控制。
- 不指定serialVersionUID(会有一个默认值):类中字段发生变更,已序列化的数据无法恢复。(新UID和旧UID不同–>InvalidClassException)
- transient:瞬态变量,字段不会被序列化。
- 序列化类中的引用类也必须序列化。
serializable与parcelable比较
- serializable通过io对硬盘操作,速度慢; parcelable在内存中操作,速度快。
- serializable大小不受限制; parcelable一般不超过1M。
- serializable大量使用反射,产生内存碎片。
6.json
**描述:**一种轻量级的数据交换格式。
gson/fastjson
gson
- @serializable:注解,转化key
- @Expose:是否参与序列化/反序列化
- 自定义TypeAdapter/自定义JsonDeserializer:解决解析异常
Gson原理
- 反射创建该类的对象
- 把json中对应的值赋值给对象属性
- 返回对象。
基于事件驱动解析:边读边解析。(区别于一次性读到内存中解析)
JsonElement子类
- JsonArray
- JsonNull
- JsonObject
- JsonPrimitive: java的基本类型
TpyeToken
**适配器模式:**eg 电源转换器
TypeAdapter: json串 <------> type
- 基本数据类型子类
- ReflectiveTypeAdapter
7.RxJava
响应式编程: 起点—步骤一-----步骤二(一的参数)------步骤三(二的参数)----> 终点 (不间断)
被观察者(observable)-------> 观察者(observer)
核心思想: 从起点开始,将事件流向终点。可以对事件拦截/改变,下一个拦截只关联上一个拦截。
应用场景
- rxjava配合retrofit
- 防抖(RxHandler)
- 网络嵌套
- doOnNext
Rxjava方法:
-
map(new Function)
-
flatMap(new Function)
-
subscribeOn(…) / observeOn(…)
-
doOnNext(new Consumer)
-
subscribe(new Observer())
事件执行中(如网络请求中),关闭页面: onDestroy中要销毁 disposable.dispose() (onSubscribe(Disposable d))
-
标准观察者模式
被观察者(拥有集合容器管理观察者) ---- 多个观察者
-
RxJava的观察者模式
多个被观察者(create/map等创建多个Observable), 一个观察者
中间有一个抽象层(发射器,转换,onNext),降低耦合度。 (中间层装"包裹",再拆"包裹",包裹嵌套)
Hook:钩子,在流程执行中,插入新的执行。
RxJavaPlugins.setIoSchedulerHandler(new Function<Scheduler, Scheduler>() {
@Override
public Scheduler apply(Scheduler scheduler) throws Exception {
Log.i("xxx","全局监听 scheduler:"+ scheduler);
return scheduler;
}
});
**线程切换:**最终都是交给线程池管理
-
subscribeOn: 给上面的代码分配线线程
-
observeOn: 给下面的代码分配线程
-
线程类型
- Schedulers.io():重用空闲的线程,比newThread效率更高。
- Schedulers.newThread():
- Schedulers.single()
- AndroidSchedulers.mainThread():android主线程
8.IO
8.1 装饰模式 (功能增强)
装饰模式: 层层包裹增强
DataOutputStream out = new DataOutputStream( //基本类型数据的输出
new BufferedOutputStream( //具备缓存功能
new FileOutputStream( //向文件中写入数据
new File(file)));
Component(抽象接口)
- ConcreateComponent:具体的构建对象,实现某个功能
- Decorator: 所有装饰器的抽象父类,继承并持有抽象接口
- concreateDecoratorA :实际的装饰对象,实现具体的功能 (可以传入 ConcreateComponent对象)
- concreateDecoratorB :实际的装饰对象,实现具体的功能
InputStream (抽象接口, read方法需要子类具体实现)
-
ByteArrayInputStream (具体的组件对象)
-
PipedInputStream (具体的组件对象)
-
FilterInputStream <==== 装饰器的抽象父类 (仅仅增加构造函数,持有InputStream)
-
BufferedInputStream <===实际的装饰器对象
byte[1024],一次性读取1024个字节(在调用磁盘一次读;否则一个字节读一次磁盘。)
-
DataInputStream <===实际的装饰器对象
-
-
FileInputStream
File/ FileDescriptor
-
ObjectInputStream
File file = new File("/xx/xx.txt");
//嵌套流对象
DataInputStream dataInputStream = new DataInputStream(
new BufferedInputStream(
new FileInputStream(file)
));
boolean b = dataInputStream.readBoolean();
byte b1 = dataInputStream.readByte();
-
字节流:inputStream/outputStream 字节流中的行也就是一个字节符号
-
字符流:Reader/Writer. readLine()
8.2 字符流:Reader/Writer
一个字符占用两个字节
//字节流 转换为 字符流
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(dataInputStream));
String str = "";
while ((str = bufferedReader.readLine()) != null){ <===============行的概念
System.out.println(str);
}
FileWriter 继承 OutputStreamWriter 依赖 FileOutputStream 依赖 File
- 构造方法:OutputStreamWriter(OutputStream out);
- 构造方法:OutputStreamWriter(OutputStream out,String CharSetName);
BufferedWriter out=new BufferedWriter(new OutputStreamWriter(System.out));
BufferedReader in= new BufferedReader(new InputStreamReader(System.in);
String line=in.readLine();
8.3 BufferedInputStream
-
缓冲区的输入流,默认缓冲区大小是8M,减少访问磁盘的次数
-
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(file)); byte[] b = new byte[1024]; while (bin.read(b, 0, b.length) != -1){ String s = new String(b); }
8.4 RandomAccessFile (随机位置文件访问)
-
从指定位置开始读,seek(起始位置)
-
网络数据下载应用:断点续传(分段 进行进行读)
在带宽不变的情况下,多线程分段下载能提高性能
-
既可以读也可以写。
RandomAccessFile rsfWriter = new RandomAccessFile(file, "rw");
//从10001开始存
rsfWriter.seek(10000);
8.5 FileChannel (NIO 管道)
-
配合ByteBuffer,操作速度更快
-
批量/缓存的方式read/write
-
FileInputStream inputStream; RandomAccessFile randomAccessFile; FileChannel channel1 = randomAccessFile.getChannel(); //获取FileChannel FileChannel channel = inputStream.getChannel();//获取FileChannel
-
效率 ≈ (Stream以byte数组方式)
Instant begin = Instant.now();
RandomAccessFile randomAccessSourceFile = new RandomAccessFile(sourceFile, "r");
RandomAccessFile randomAccessTargetFile = new RandomAccessFile(targetFile, "rw");
FileChannel sourceFileChannel = randomAccessSourceFile.getChannel();
FileChannel targetFileChannel = randomAccessTargetFile.getChannel();
//ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024*1024);
while(sourceFileChannel.read(byteBuffer) != -1) {
byteBuffer.flip();
targetFileChannel.write(byteBuffer);
byteBuffer.clear();
}
System.out.println("use time: " + Duration.between(begin, Instant.now()).toMillis());