这是我参与「第四届青训营 」笔记创作活动的第8天
课堂笔记
1.对象
Java对象和类的内存结构
当我们通过new创建一个Java对象时,虚拟机会安排内存分配的所有工作。
Java对象头
所有Java类最终的父类都是java.lang.Object,因此当我们创建一个Java对象时,必然伴随着java.lang.Object的实例化过程。Java.lang.Object在ART中有个对应的C++类art::mirror::Object,命名空间中有"mirror",表示其和Java类之间存在对应关系。当我们通过new Object()来创建一个java对象时,就会在内存空间得到一个最简单的内存结构。
Java类头
Java中提供了一个java.lang.Class类,该类的实例表示一个运行程序中的类或接口。实例中的字段记录了类或接口的元数据。
当我们想要创建一个java.lang.Class类的实例(类对象)时,以下三种方法可供选择:
- Class.forName("className")
- MyClass.class
- obj.getClass()
classloader&双亲继承
-
ClassLoader是一个抽象类,其中定义了ClassLoader的主要功能;
-
SecureClassLoader继承了抽象类ClassLoader;
它并不是ClassLoader的实现类,而是拓展了ClassLoader类加入了权限方面的功能,加强了安全性。 -
URLClassLoader类继承自SecureClassLoader,用来通过URl路径从jar文件和文件夹中加载类和资源;
-
ExtClassLoader和AppClassLoader都继承自URLClassLoader,它们都是Launcher 的内部类;
Launcher 是Java虚拟机的入口应用,ExtClassLoader和AppClassLoader都是在Launcher中进行初始化的。
2.执行
- 执行包括哪些方式 - JIT/AOT/解释
- 栈管理
- 异常处理
- 多线程
执行方式
栈管理
异常
背景
熟悉Android开发的同学都知道,如果我们应用程序中发生了java层的崩溃,我们可以通过下面方式捕获:
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
Log.i("jin", "uncaughtException " + e.getMessage());
// do some something
}
});
解释器
int i = 9 / 0;
Log.i("jin", "exception : " ,new Exception())
0023: const/16 v0, #int 9 // #9
0025: div-int/lit8 v0, v0, #int 0 // #00
0027: new-instance v1, Ljava/lang/Exception; // type@0db2
0029: invoke-direct {v1}, Ljava/lang/Exception;.<init>:()V // method@fca9
002c: const-string v2, "jin" // string@6a64
002e: const-string v3, "exception" // string@5549
0030: invoke-static {v2, v3, v1}, Landroid/util/Log;.i:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I // method@0139
0033: return-void
我们可以看到int i = 9 / 0; 这段代码其实执行的就是div-int字节码。
多线程
mutex和spin lock的区别
有哪些内核锁机制?
(1)原子操作
(2)自旋锁
(3)信号量与互斥量
课后总结
我们的对象是怎么分配出来的?
-
对象分配的大小怎么确定的 -类
-
对象怎么分配的 - 内存分配
-
对象怎么回收 - 内存回收
虚拟机为了保证我们的代码高效顺利执行,需要提供哪些机制?
-
- JIT/AOT
-
- 栈管理
-
- 异常
-
- 多线程
思考:mutex和spin lock的选择?
-
Mutex适合对锁操作非常频繁的场景,并且具有更好的适应性。尽管相比spin lock它会花费更多的开销,但是它能适合实际开发中复杂的应用场景,在保证一定性能的前提下提供更大的灵活度。
-
spin lock的lock/unlock性能更好,但是它只适应用于临界区运行时间很短的场景。而在实际软件开发中,除非程序员对自己的程序的锁操作行为非常的了解,否则使用spin lock不是一个好主意(通常一个多线程程序中对锁的操作有数以万次,如果失败的锁操作(contended lock requests)过多的话就会浪费很多的时间进行空等待。
-
更保险的方法或许是先使用 Mutex,然后如果对性能还有进一步的需求,可以尝试使用spin lock进行调优。毕竟我们的程序不像Linux kernel那样对性能需求那么高。