android 相关的JVM,GC 知识的扩充2

本文详细探讨了Java内存的堆栈管理,解释了为何JVM使用堆来存储对象,以及JVM与操作系统的堆栈区别。讨论了Java虚拟机的生命周期、main方法与虚拟机的关系、线程类型以及GC垃圾回收机制。还涉及类加载器的双亲委派模型,以及Android中findclass的调用流程,强调了内存泄漏问题和静态内部类的内存影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.java自动管理堆(heap)和(栈),程序员不能直接的设置堆和栈。

2.操作系统的堆和栈:

  • 堆(操作系统):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收,分配方式类似于链表。

  • 栈(操作系统):由操作系统自动分配释放,存放函数的参数值,局部变量值等。操作方式与数据结构中的栈相类似。

1. 2.为什么jvm的内存是分布在操作系统的堆中呢??

因为操作系统的栈是操作系统管理的,它随时会被回收,所以如果jvm放在栈中,那java的一个null对象就很难确定会被谁回收了,那gc的存在就一点意义都莫有了,而要对栈做到自动释放也是jvm需要考虑的,所以放在堆中就最合适不过了。

上图表明:jvm虚拟机位于操作系统的堆中,并且,程序员写好的类加载到虚拟机执行的过程是:当一个classLoder启动的时候,classLoader的生存地点在jvm中的堆,然后它会去主机硬盘上将A.class装载到jvm的方法区,方法区中的这个字节文件会被虚拟机拿来new A字节码(),然后在堆内存生成了一个A字节码的对象,然后A字节码这个内存文件有两个引用一个指向A的class对象,一个指向加载自己的classLoader

5,java虚拟机的生命周期:声明周期起点是当一个java应用main函数启动时虚拟机也同时被启动,而只有当在虚拟机实例中的所有非守护进程都结束时,java虚拟机实例才结束生命。

6,java虚拟机与main方法的关系:main函数就是一个java应用的入口,main函数被执行时,java虚拟机就启动了。启动了几个main函数就启动了几个java应用,同时也启动了几个java的虚拟机。

7,java的虚拟机种有两种线程,一种叫叫守护线程,一种叫非守护线程(也叫普通线程),main函数就是个非守护线程,虚拟机的gc就是一个守护线程。java的虚拟机中,只要有任何非守护线程还没有结束,java虚拟机的实例都不会退出,所以即使main函数这个非守护线程退出,但是由于在main函数中启动的匿名线程也是非守护线程,它还没有结束,所以jvm没办法退出.

8,虚拟机的gc(垃圾回收机制)就是一个典型的守护线程。

 9,实例理解“当所有的非守护线程全部结束,jvm声明周期才结束”:

public class MianAndThread{
    public static void main( String args[]){
        new Thread(new Runnable(){
                    @override
                    public void run(){
                        Thread.currendThread.sleep(5000s);
                        System.out.println("睡了5s后打印,这是出main之外的非守护线程,这个推出后这个引用结束,jvm声明周期结束。任务管理的java/javaw.exe进程结束"
                    }
        }
        System.out.println("mian线程直接打印,mian线程结束,电脑任务管理器的java/javaw.exe进程并没有结束。")
    }   
}

10,GC垃圾回收机制不是创建的变量为空是就被立刻回收,而是超出变量的作用域后就被自动回收。

11,程序在jvm原先的流程:

首先,当一个程序启动之前,它的class会被类装载器装入方法区 ,执行引擎读取方法区的字节码自适应解析,边解析就边运行(其中一种方式),然后pc寄存器指向了main函数所在位置,虚拟机开始为main函数在java栈中预留一个栈帧(每个方法都对应一个栈帧),然后开始跑main函数,main函数里的代码被执行引擎映射成本地操作系统里相应的实现,然后调用本地方法接口,本地方法运行的时候,操纵系统会为本地方法分配本地方法栈,用来储存一些临时变量,然后运行本地方法,调用操作系统APIi等等。

12,根据Java虚拟机规范的规定,如果方法区的内存空间不能满足内存分配需要时,将抛出OutOfMemoryError异常。

13,双亲委派机制:JVM在加载类时默认采用的是双亲委派机制。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

 例如:当jvm要加载Test.class的时候,

  (1)首先会到自定义加载器中查找(其实是看运行时数据区的方法区有没有加载),看是否已经加载过,如果已经加载过,则返回字节码。

  (2)如果自定义加载器没有加载过,则询问上一层加载器(即AppClassLoader)是否已经加载过Test.class。

  (3)如果没有加载过,则询问上一层加载器(ExtClassLoader)是否已经加载过。

  (4)如果没有加载过,则继续询问上一层加载(BoopStrap ClassLoader)是否已经加载过。

  (5)如果BoopStrap ClassLoader依然没有加载过,则到自己指定类加载路径下("sun.boot.class.path")查看是否有Test.class字节码,有则返回,没有通

知下一层加载器ExtClassLoader到自己指定的类加载路径下(java.ext.dirs)查看。

  (6)依次类推,最后到自定义类加载器指定的路径还没有找到Test.class字节码,则抛出异常ClassNotFoundException。

为什么要使用这种加载方式呢?

1,类加载器代码本身也是java类,因此类加载器本身也是要被加载的,因此显然必须有第一个类加载器不是Java类,这就是bootStrap,是使用c++写的其他这是java了。

2,虽说bootStrap、extclassLoader、appclassloader三个是父子类加载器关系,但是并没有使用继承,而是使用了组合关系。

3,优点,具备了一种带优先级的层次关系,越是基础的类,越是被上层的类加载器进行加载,可以比较笼统的说像jdk自带的几个jar包肯定是位于最顶级的,再就是我们引用的包,最后是我们自己写的,保证了java程序的稳定性。

20,JVM运行简易过程:

  上图左半部分其实不是在JVM中,程序员在eclipse上写的是.java文件,经过编译成.class文件(比如maven工程需要maven install,打成jar报,jar包里面都是.calss文件);这些步骤都是在eclipse上进行的。然后类加载器(classloader)一直到解释器是属于JVM的

二  从内存的角度看Jvm

1 声明一个普通类person,类里面没有任何参数。

 可以看到person对象有8个字节。

2 如何我们把Person类定义成内部类呢

 

 这里就多了4个字节,这4个字节就是该对象持有外部类的引用。

3 如果我们把对象的声明写成静态的会发生什么?

 person引用是静态的,所以这个person就属于类的一部分,而这个引用在有持有外部类的引用,如果说这段代码是在activity中写的,那么就相当于持有activity的引用,但是类的生命周期是整个jvm的,这会导致person的生命周期很长但是却持有了activity这种短生命周期的引用。就会导致内存泄漏。如何去解决呢?

4 将类的改为静态内部类。

 

从内存看引用的内存又恢复了8个字节,不会去持有外部类的引用了。 

三 android虚拟机底层跟踪findclass

1 jni调用findclass开始

 2 一直跟踪到具体实现到了class_linker.cc中

 3 细节跟踪

// Find the class in the loaded classes table.
mirror::Class* klass = LookupClass(descriptor, class_loader);

从谷歌工程师上的注解看到,这一步是从class类表中找到指定class

lookupclass() 

从已加载的dex缓存中读取

defineClass()如何没有从lookupClass 方法缓存中读取到class信息就开始完整的去从磁盘中读取

第一步:引出了一个类的“模型”,就是kclass

第二步 模型容器准备好了就需要在里面装“内容”了,调用LoadClass去装载

 将磁盘中dex文件中读出的内容装到klass里面。

将各种参数和方法读到对应集合中。

 静态变量,对象变量,基本变量撞到field集合

 对应的不同类型方法装到方法集合。ArtMethod(方法其实就是一堆art指定的集合)

findclass 调用流程图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值