打开 ART 虚拟机的大门 | 青训营笔记

98 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第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对象时,就会在内存空间得到一个最简单的内存结构。

image.png

Java类头

Java中提供了一个java.lang.Class类,该类的实例表示一个运行程序中的类或接口。实例中的字段记录了类或接口的元数据。

当我们想要创建一个java.lang.Class类的实例(类对象)时,以下三种方法可供选择:

  1. Class.forName("className")
  2. MyClass.class
  3. obj.getClass()

classloader&双亲继承

image.png

  • 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那样对性能需求那么高。