如何应对Android面试官->JVM对象回收与逃逸分析

android 常用JVM配置参数 Trace跟踪参数 -verbose:gc -XX:+printGC 可以打印GC的简要信息 [GC 4790K->374K(15872K), 0.0001606 secs] [GC 4790K->374K(15872K), 0.0001474 secs] [GC 4790K->374K(15872K), 0.0001563 secs] [GC 4790K->374K(15... 阅读详情

前言

本节主要围绕下面三个方向进行知识点的讲解,带你从架构师角度认识内存溢出

虚拟机中对象的创建过程

当 JVM 遇到一条字节码 new 指令的时候,它首先会进行:

检查加载

检查对应的类有没有加载进来,如果没有加载进来,则要重新进行类加载,直到类加载成功,成功之后继续进行检查加载,具体检查什么呢?

Object obj = new Object();

例如上面这段代码,它会检查通过设置的 new 的参数(new Object)是否能在方法区的常量池中找到这个类的符号引用,

什么是类的符号引用?

用一组符号来描述你所引用的对象,例如 NBA 球员 James,这个 James 就是一个符号引用,假设 James 不来中国,那么你是看不到他的,你只看到了这个符号;那么对于类来说,这个类的前面加了 com.american.James,同时它要检查这个 James 类有没有被加载过;

分配内存

检查加载成功之后,就要开始分配内存,那么 JVM 是如何划分内存的呢?对象申请内存空间流程是怎样的呢?

划分内存有两种方式,一种是指针碰撞、一种是空闲列表

指针碰撞

假设我们的堆内存比较规整,红色代表已经分配了内存,白色代表未分配的内存,这个堆内存比较规整,我们可以使用一个指针指向堆内存中的最后一个对象的偏移量,当我们给一个对象申请内存空间的时候,这个指针就会根据这个对象的 size 挪动到指定的位置来放下创建的这个对象,这个就叫做指针碰撞(移动一个对象大小的距离);另外这种指针碰撞只能在堆空间比较规整的情况下,但是经过垃圾回收之后,就会变成零散的,不规整的,那么指针碰撞在这种情况下就不合适了,这种时候,JVM 就会维护一个空闲列表;

空闲列表

JVM 用空闲列表来标记对应的位置是否有对象存在,在分配位置的时候,假设对象需要一个位置大小,就分配到 1 的位置,如果需要三个大小,就分配到 3-5 这个位置;

上面两种划分方式都是因为:在 JVM 中对象要占据的内存一定要是连续的;

JVM 用哪种方式来划分,取决于堆的规整程度;堆空间的规整度 又是由垃圾回收器决定的,垃圾回收器是否带有整理功能;

不管使用 指针碰撞 还是 空闲列表,JVM 为了提高效率,同样使用了多线程,那么就会带来多线程安全问题,那么 JVM 是如何解决并发安全的问题呢?

CAS加失败重试

A B 两个分配内存的时候,都会去抢同一块内存,会进行查询操作,查询下这块空间是不是空的,A B 两个线程拿到的都是空的,就会进行 CAS 操作,因为 CAS 操作是由 CPU 保证线程的执行顺序,假设 A  比 B 先执行,当 A 进行 CAS 操作的时候,判断这块空间是空的,就进行交换占据这块内存,当 B 进行 CAS 操作的时候,比较发现这块区域不空了,就会进行重试,直到找到一块为空的区域,然后进行交换操作;

CAS原理可以查看之前的讲解:如何应对Android面试官->CAS基本原理

本地线程分配缓冲

CAS 比较并且交换,比较和交换,必定耗费性能,所以 JVM 提供了第二种方式:本地线程分配缓冲(Thread Local Allocation Buffer)简称 TLAB;

本地线程分配缓冲类似 ThreadLocal,堆中的 eden 预先给每个线程划分单独的一块区域,当线程执行的时候,直接分配,就不需要采取安全措施,这就是本地线程分配缓冲,但是 TLAB 比较小,只占用 eden 区的 1%;

什么时候 CAS,什么时候分配缓冲?

分配缓冲默认开启,如果要禁用,可以使用下面的配置选项

-XX:-UseTLAB 

内存空间初始化

内存空间的初始化不是构造方法,而是在内存分配之后,划分了一块区域,但是这块区域是空的,需要把里面的一些数据设置为 零值,这一步确保了对象在分配完内存后,在代码里面不需要赋值就可以直接使用,程序越早使用对象,它的效率就越高,这就是内存空间初始化;

什么是零值?

比如 int 类型,那么它的零值就是 0,boolean 类型,它的零值就是 false,

设置

对象属于哪个实例,需要设置一下,以及设置对象头;

对象的初始化

调用构造方法进行对象的初始化;

以上过程,针对的是 一般的对象(也就是我们编写的程序),因为 Java 中万物介对象;

虚拟机中对象的布局

HotSpot 中对象可以分为三块:对象头、实际数据、对齐补充

对象头

Mark Word(存储对象自身的运行时数据)

哈希码、GC分代年龄、锁状态标记、线程持有的锁、偏向线程ID、偏向时间戳;

类型指针

指向类对象信息的指针;

Person p = new Person(); 

p 是一个引用对象,存在栈中(java虚拟机栈中的栈桢),new Person() 存在于堆中, 假设有一个 A 类,A 类中有一个 Person 对象,这个对象存储于堆区,那么它就会指向方法区的这个 A类(方法区存储类的描述信息),指向的过程就是这个 Class Pointer;

若为对象数组,还应有记录数组长度的数据

lenght 数据长度,只针对数组对象;

实例数据

包含对象所有成员变量,根据变量类型决定大小;

对齐填充

为了让对象的大小为8字节的整数倍;

为什么要对齐填充?

因为在 HotSpot 中,它对管理的对象的大小是有要求的,必须是 8 字节的整数,但是 对象头和实例数据是没有办法控制的,假设对象头 + 实例数据刚好 38 字节,那么对齐填充就会填充 2 个字节,如果对象头和实例数据加起来刚好是 8 的整数倍,那么这个对齐填充就不需要了;填充的话随便用一些值填充就可以了;

虚拟机中对象的访问定位

所有的虚拟机中(包括 HosSpot)对象的访问定位都有两种方式:使用句柄、直接指针

使用句柄

什么是句柄?

句柄就是在堆空间划一块区域,叫作句柄池,句柄池中存放的是什么呢?对象的访问通过 reference ,这个 reference 中就不会存放对象的地址了,而是存放一个叫作对象实例的指针,句柄其实就是做了一次中转,通过句柄池找到真实的对象实例数据,这样做的好处就是:如果对象进行了移动,句柄池不需要修改,还是可以通过句柄池找到对应的对象实例;

例如:句柄池中存放的是 Kobe 的对象实例指针,实例池中存放的是 Kobe 的实例,但是 Kobe 离开之后,句柄池的这个指针不需要修改,当有一个新的 Kobe 实例被替换的时候,还是可以通过这个指针找到对应的 Kobe 实例;

但是这样做的坏处是需要通过 Auth 查找;通过这个句柄池再映射一次,会有一次额外的指针定位开销,虽然这个开销比较小,但是 JVM 中对象的创建是比较疯狂的,这块会存在一个积少成多,那么虚拟机又提供了另外一种方式:直接指针;

直接指针

Person p = new Person();

在 HotSpot 中 使用的就是直接指针,这个 p 就是一个引用,这个引用就会指向真实的地址;这样做虽然带来了效率的提升,但是如果对象一直被移来移去,对象在物理区 移来移去,那么这个 reference 就会进行改变;

如何判断对象的存活

在 JVM 中对象是可以被回收的,首先对象是在堆中进行分配的,如果堆空间满了,就会触发垃圾回收,但是在进行垃圾回收之前我们要确定哪些对象是存活的;怎么判断呢?大部分都是采用的下面这两种方式

引用计数法

用一个计数器来统计对象被引用,对象被引用了,计数器就+1,如果这个引用失效了,就 -1,如果等于 0 说明这个对象不被引用了;

这里会存在一个问题:对象的相互引用;

上图中的两个对象就存在相互引用,但是又跟运行方法里面的不相关,外部没有可用的地方与它进行连接,它其实也是死的;

可达性分析(根可达)

JVM 中用的就是可达性分析法,本质上是根据一条链路来追踪的,这条链路以 GC Roots 的变量(静态变量、线程栈变量、常量池变量、JNI指针变量)或者对象(class、Exception、OOM、类加载器、加锁 synchronized 对象、JMXBean、临时性)为根节点,形成引用链路的则为存活对象,没有被 GC Roots 直接引用或者间接引用的都是可以回收的对象;

通过下面的代码可以验证 HotSpot 使用的是可达性分析法

// -XX:+PrintGC
public class ReliabilityAnalysisTest {    
    public Object instance = null; 
    // 辅助作用,占据内存,用来可达性分析   
    private byte[] bigSize = new byte[10 * 1024 * 1024];    
    public static void main(String[] args) {        
        ReliabilityAnalysisTest test = new ReliabilityAnalysisTest();        
        ReliabilityAnalysisTest test1 = new ReliabilityAnalysisTest();        
        // 相互引用        
        test.instance = test1;        
        test1.instance = test;        
        // 解除引用        
        test.instance = null;        
        test1.instance = null;        
        // 回收内存        
        System.gc();    
    }
}

VM参数加上注释的配置信息 -XX:+PrintGC 运行之后可以看到,内存进行了回收,说明引用计数法的方式在 HotSpot 中没有被使用;如果是可达性分析的话,这两个对象必然不会被回收;

可达性分析算法之后,没有引用链,但是互相引用的对象,也不是立马就会被回收,它们其实处于缓刑状态,还是可以被挽救的,但是这个挽救是需要开发者通过代码实现的,但是 finalize 只能执行一次,可以看下面的代码示例;

public class FinalizeTest {    
    public static FinalizeTest instance;    
    public void isAlive() {        
        System.out.println("is Alive");    
    }    
    @Override    
    protected void finalize() throws Throwable {        
        super.finalize();        
        System.out.println("finalize execute");        
        FinalizeTest.instance = this;    
    }    
    public static void main(String[] args) throws InterruptedException {        
        instance = new FinalizeTest();        
        // 第一次 GC        
        instance = null;        
        System.gc();        
        Thread.sleep(1000); // 等待 finalize 方法执行        
        if (instance != null) {            
            System.out.println("第一次GC后,对象实例不为空");            
            instance.isAlive();        
        } else {            
            System.out.println("第一次GC后,对象实例为空");        
        }        
        // 第二次 GC        
        instance = null;        
        System.gc();        
        Thread.sleep(1000); // 等待 finalize 方法执行        
        if (instance != null) {            
            System.out.println("第二次GC后,对象实例不为空");            
            instance.isAlive();        
        } else {            
            System.out.println("第二次GC后,对象实例为空");        
        }    
    }
}

可以看到第一次 GC 后,执行了 finalize 方法,进行了拯救,但是第二次 GC 之后,就被回收了;

这里为什么要加 sleep,是因为 finalize 的线程优先级非常低,如果去掉 sleep 则拯救不成功;

可以看到第一次 GC 之后就被回收了;

所以 finalize 尽量不要使用,这个方法太不可靠了;

JVM中的引用类型

强引用

Object obj = new Object();

这种就是强引用,只要 GC Roots 还在,那么强引用的就不会被回收;

软引用

内存不足,将要发生OOM的时候,会被回收;可以查看下面的代码示例

// -Xms20M -Xmx20
Mpublic class SoftReferencesTest {    
    static class User {        
        private String name;        
        private int age;        
        public User(String name, int age) {            
            this.name = name;            
            this.age = age;        
        }        
        @Override        
        public String toString() {            
            return "User{" +                    
                    "name='" + name + '\'' +                    
                    ", age=" + age +                    
                '}';        
        }
    }    
    public static void main(String[] args) {        
        User user = new User("张三", 18);        
        SoftReference<User> softReference = new SoftReference<>(user);        
        System.out.println("GC 前读取:" + softReference.get());        
        user = null; // 置空,确保只有软引用指向该对象        
        System.gc(); // 手动触发GC        
        System.out.println("GC 后读取:" + softReference.get());        
        // 构造内存溢出        
        List<byte[]> list = new ArrayList<>();        
        try {            
            for (int i = 0; i < 10000; i++) {                
                list.add(new byte[1024 * 1024]);            
            }        
        } catch (Throwable e) {            
            System.out.println("内存溢出:" + softReference.get());        
        }    
    }
}

可以看到,当发生内存溢出的时候,被回收掉了,这个时候我们获取弱引用中的数据是拿不到的;

弱引用

GC 扫描到了就会回收;

public class WeakReferencesTest {    
    static class User {        
        private String name;        
        private int age;        
        public User(String name, int age) {            
            this.name = name;            
            this.age = age;        
        }        
        @Override        
        public String toString() {            
            return "User{" +                    
                "name='" + name + '\'' +                    
                ", age=" + age +                    
                '}';        
        }    
    }    

    public static void main(String[] args) {        
        User user = new User("张三", 18);        
        WeakReference<User> weakReference = new WeakReference(user);        
        System.out.println("GC 前读取:" + weakReference.get());        
        user = null; // 置空,确保只有软引用指向该对象        
        System.gc(); // 手动触发GC        
        System.out.println("GC 后读取:" + weakReference.get());    
    }
}

可以看到 GC 的时候,就被回收掉了;

虚引用

随时都会被回收,不知道什么时候就被回收了;主要用来监控垃圾回收器是否正常工作,一般业务开发中用不到;

对象申请内存空间流程

对象的分配原则

  • 对象优先在Eden分配;
  • 空间分配担保;
  • 大对象直接进入老年代;
  • 长期存活的对象进入老年代;
  • 动态对象年龄判定;

对象分配时的优化技术

当我们 new 一个对象的时候,JVM 的第一个优化就是:是否栈上分配?

通常我们总是说:几乎所有对象都是堆中分配,但不是 100%,它也可以在栈上分配,并且在栈上分配的对象,就不需要垃圾回收,这也是为什么方法要在栈中执行的原因,效率高,栈的内存是跟随线程的,线程执行完了,这个栈也就结束了;

如果想在栈上分配对象,HotSpot 需要一项技术:逃逸分析技术;

逃逸分析:判断方法的对象有没有逃逸,就是分析这个对象的作用域

  • 是不是可以逃逸出方法体;
  • 是不是可以逃逸出其他线程;

可以看下面代码示例

// -XX:+PrintGC
// -XX:-DoEscapeAnalysis
public class EscapedAnalysisTest {    
    public static void main(String[] args) throws InterruptedException {        
        long start = System.currentTimeMillis();        
        for (int i = 0; i < 6_000_000_0; i++) {                
            allocate();        
        }        
        System.out.println("Escaped Analysis: " + (System.currentTimeMillis() - start));        
        Thread.sleep(60_000);    
    }    

    static void allocate() {        
        Person person = new Person(1000L, 2000L);    
    }    
    
    static class Person {        
        private long age;        
        private long height;        
        public Person(long age, long height) {
            this.age = age;
            this.height = height;        
        }    
    }
}
Person person = new Person(1000L, 2000L); 

person 就会被分配到栈上,它满足 不会逃逸出方法体(方法外没有调用),也不会逃逸出其他线程(只有一个main线程);

-XX:-DoEscapeAnalysis // 关闭逃逸分析

如果不想使用栈上分配(不做逃逸分析)的运行结果,可以加上上面的配置信息;执行结果如下

可以看到触发了 GC;

JVM的第二个优化就是:堆中本地线程分配缓冲,

上面已经介绍了;

对象优先在 Eden 区分配

如果不支持 本地线程分配缓冲,会判断是不是大对象,如果不是大对象,则在 Eden 区分配,满足了对象优先在 Eden 区分配的原则之一,如果是大对象,则直接分配到老年代(满足了大对象直接进入老年代原则之一)

大对象:一般是很长很长的字符串、数组;

如果我们通过参数 -Xms30M -Xmx30M 来设置我们的JVM 堆区为 30M,那么老年代就会分配20M,Eden区分配 8M,From区分配 1M,  To区分配 1M;

也就是说新生代只占堆内存的三分之一,所以说大对象放到老年代可以避免垃圾回收;

JVM 可以通过参数设置是否为大对象,-XX:+PretenureSizeThresold10M,大于等于10M的对象则认为是大对象,直接分配到老年代;

Eden 上分配之后还会遵循一个原则:长期存活的对象进入老年代;

当触发垃圾回收的时候,因为 Eden 区只存放新生对象,Eden 中所有存活的对象都将被移动到 From 区,对象的对象头中的 age(Mark Word 区域的 GC分代年龄) 就会 +1,然后 Eden 被清空,Eden 被清空,当再次充满的时候,所有存活的对象和 From survivor 中所有存活的对象都被移动到 To survivor,然后 Eden 和 From survivor 被清空,这个时候 To 中的对象的对象头中的 age 会再次 +1 (=2),当再次充满触发垃圾回收的时候,会把存活的对象和 To survivor 中所有存活的对象都被移动到 From survivor,然后 Eden 和 To survivor 被清空,这个时候 From 中的对象的对象头中的 age 会再次 +1(=3),From 和 To 循环往复,当 age = 15 的时候,会被移动到老年代(满足了长期存活对象进入老年代原则之一),这种循环往复采用的就是 复制回收 算法;

JVM 为什么不把 Eden、From、To 合并成两个,只保留 From和 To呢?

这是因为复制回收算法要浪费一半的空间,为什么要浪费一半呢?万一复制过去的全是存活对象,比如从 From 复制到 To 的都是存活对象,但是 To 中没有足够的空间容纳下这些对象了;所以往往复制算法的空间都是一分为二,导致内存利用率只有50%;Oracle 和 Sun 公司做过大数据统计,90% 的对象在被垃圾回收的时候都能回收掉,只剩 10% 的存活对象,这 10% 的存活对象放入 From 区,那么就需要一个对等的 To 区,所以采用这样的一种方式的垃圾回收,那么浪费的只有 10% 的空间,空间利用率可以达到 90%;所以就没必要采用标准的复制回收,把堆区一分为二,而是分成三份区域,第一次垃圾回收的时候,移动到 From 区,后续采用标准的复制回收算法,从 From 复制到 To 区;

JVM 的复制回收算法为什么是 15 次,才会移动到老年代,可以修改这个值吗?

JDK 提供的 markOop.hpp(也就是 Mark Word) 文件中有提及到:

不管是 32 位的虚拟机还是 64 位的虚拟机,这个 age 都是存放的 4 位,从二进制来看存放的最大值就是 1111,按照十六进制转换,就是 15,所以说复制回收 age 的最大次数默认是 15 次;

JVM 也提供修改参数,可以修改这个值:

-XX:MaxTenuringThresold = 10 // 就可以修改这个值;

进入老年代的对象,age 就不会在被标记 +1;

垃圾回收的两个概念

在进行垃圾回收的时候,它其实是有两个概念的,在进行分代的时候,它可以采用两种 GC,垃圾回收器回收新生代称之为 Minor GC,回收老年代称之为 Major GC;

空间分配担保

通过堆中的对象分配原则,对象在分配的时候有 Eden 区 进入 From 区或者 To 区,最后进入  Tenured 区,大部分情况下老年代的对象都是由新生代晋级来的,但是假设老年代就只剩下 1M 的空间了,然后还有从 From 或者 To 区做一个对象的晋级,或者通过大对象分配,但是在进行对象晋级或者大对象分配,不能保证一定会有足够的空间来存放,所以在每一次晋级或者大对象分配的时候,自身要做一次Major GC,这种方式比较安全,但是 JVM 认为这种很影响效率,所以 JVM 就提出了一个概念叫作:空间分配担保,这个担保由 JVM 来担保,放心分配,如果确实不够了,再进行一次Major GC,而不用每次晋级都要触发,这就满足了对象分配空间分配担保原则之一;

动态年龄判断

为了优化 From 区和 To 区,因为这两个区域本身也不大,假设 From 区中有三个对象,这三个对象的年龄加起来仅仅是5,但是这三个对象占据了 From 区的一半,那么这个时候它会走一个动态年龄判断,并不一定非要达到15,就会让这个几个对象提前晋级到老年代,这些对象就不需要等到15之后再进入老年代;

整体申请内存空间流程

  • 先去 eden 区看看是否有足够的空间;
    • 有,直接分配
  • 无,JVM 开始回收垃圾对象,回收完成之后,判断 eden 是否有足够空间;
    • 有,直接分配;
  • 无,s 区域是否有足够空间;
    • 有,eden 区的存活对象移动到 s 区,新对象就可以在 eden 申请成功;
  • 无,启用担保机制,old 区是否足够空间;
    • 有,将s区的存活对象移动到 old 区,eden将存活对象放到s区,申请成功;
  • 无,JVM 触发 full gc,gc 之后查看 old 区是否有足够空间;
    • 有,将s区的存活对象移动到old区,eden将存活对象放到s区,申请成功;
  • 无,OOM;

简历润色

简历上可写:深度理解JVM内存分配原理,能基于分配原理进行深度优化;

下一章预告

带你玩转垃圾回收;

欢迎三连

来都来了,点个赞,点个关注吧~~~你的支持是我最大的动力

拼多多Android面试题及参考答案 Android 系统采用分层架构设计,从底层到上层依次为Linux 内核层硬件抽象层(HAL)系统运行时库层应用框架层应用层,共五层。位置:Linux 内核层与系统运行时库层之间,是“硬件与系统的隔离层”。核心作用:屏蔽硬件差异,为上层提供统一的硬件访问接口,解决 Android 开源与硬件厂商闭源的矛盾,具体功能如下:硬件能力抽象:将硬件的具体实现(如不同厂商的摄像头芯片、音频芯片)封装为标准化接口(如Camera HALAudio HAL。 阅读详情

相关推荐

RK3572-Android16-记录一次OTA升级失败解决方案

这里记录一次解决RK3576 Android16 OTA 用Recovery 升级失败的一次解决案例,将部分经验作为总结。本次OTA升级耗时了几天,最终是RK给出的解决方案,再加上经验来解决了其它部分问题新平台,高一点的Android版本,可能本身有bug,多请教平台来,不要蒙着干,干不出来的。

十年饮冰,难凉热血 127

什么是逃逸分析?

逃逸分析(Escape Analysis)是一种Java虚拟机的优化技术,它的主要目的是识别出那些不会逃逸出方法作用域的对象,从而可以对这些对象进行优化,提高程序的性能和效率。

杨村的博客 517

DSMA时序修复避坑指南:当Setup和Hold互卡时,除了调Margin还能做什么?

本文深入探讨DSMA时序修复中的Setup与Hold互锁问题,提供7种高阶策略,包括优先级判断、高级参数组合、单元类型智能替换等,帮助工程师突破时序收敛瓶颈。特别针对fix_eco_timing命令的隐藏参数和物理协同优化进行详细解析,助力芯片设计实现时序收敛。

weixin_30162121的博客 296

android逃逸技术汇编,Android 勒索软件的逃逸和分析对抗技术(下篇)

Android 勒索软件的逃逸和分析对抗技术(下篇)2015-08-27 10:07:06阅读:0次在本文的上篇中,我们详细介绍了Android勒索软件常见的逃避检测的手法,在本文中,我们将继续介绍这些勒索软件用来对抗分析的各种方法。分析对抗技术Android勒索软件还会利用各种手段,来尽量降低静态分析工具和框架的速度,其中包括:1.故意使用含义不清的变量名这种方法,旨在产生具有相同名称的多区段组...

weixin_42301769的博客 320

MFC界面库BCGControlBar v32.0 - 对话框、仪表盘控件升级

亲爱的BCGSoft用户,我们非常高兴地宣布BCGControlBar Professional for MFC和BCGSuite for MFCv32.0正式发布!新版本支持Windows 11、增强功能区简化模式、改进控件外观等,以及其他新功能和改进。需要最新版的可以点击这里【BCG下载】 BCGControlBar Pro for MFC v32.0正式版下载 对话框和表单 1. 添加了一个新的全局标志globalData.m_bAutoPrepareComboListStyles:如果您希望.

需要界面开发、IDE工具研发中文教程资料的小伙伴,记得私信我~ 1147

小红书图文AI创作:输入主题,一键生成图文(coze工作流教程+获取)

输入主题,工作流一键生成小红书风格图文,30s一条,可批量生成

2301_79384090的博客 2800

逃逸分析(escape Analysis)

逃逸分析----在计算机语言编译器语言优化管理中,分析指针动态范围的方法称之为逃逸分析(通俗点讲,当一个对象                    的指针被多个方法或线程引用时)---我们称这个指针发生了逃逸 public class G { public static B b; public void globalVariablePointerEscape(){//给全局变量赋值,发生

guomeijuan916的专栏 7498

【机器人】机械臂控制入门技术栈分享

机械臂控制入门技术栈分享1.前言2.机器人控制入门技术栈2.1.空间描述和变化2.2.运动学分析2.3.动力学建模2.4.控制入门学习3.进阶部分3.1.机器人参数辨识3.2.非线性控制3.3.现代机器人描述方法:指数坐标4.总结 1.前言 今天看到优快云上有个分享个人技术栈的活动,虽然我不是一个正规的程序员,也不搞前端、后端这些,但我一直在优快云上通过写博客对我的学习进行总结,于是我突发奇想,不如我也写一下我的个人技术栈,针对机械臂控制入门的,因为我水平也不高,所以本文内容也许不够饱满,也不够准确,但

要恰饭得嘛 5858

细说逃逸分析及基于逃逸分析的优化

Java 中 Iterable 对象的 foreach 循环遍历是一个语法糖,Java 编译器会将该语法编译为调用 Itreable 对象的 iterator 方法,并用所返回的 Iterator 对象的 hasNext 以及 next 方法,来完成遍历。 public void forEach(ArrayList<Object> list, Consumer<Object> f) { for (Object obj : list) { f.accept(obj);

@另维 824

mapbox 从入门到精通 - 目录

欢迎大家来到gis分享者的《mapbox 从入门到精通》专栏。博主列出学习大纲,如果有什么问题,欢迎留言。

我哎GIS博客 5847

BLDC&PMSM电机控制器硬件设计工程(八)72V 10kW电机控制器原理图工程及库文件

电机控制器基本需求:接口类型;MCU平台;通信接口;供电;信号采集;信号输出;功率;EMC或者认证需求。

小幽余生不加糖 373

Android 高级工程师面试:JVM 内存与 GC 近1年高频追问 22 题

本篇聚焦 Android 面试中的 JVM 内存与 GC 体系,面向备战高级工程师岗位的应用开发者。覆盖基础 8 题、进阶 7 题、核心 7 题,从堆栈分区、GC 类型、OOM 分类到泄漏排查与 Bitmap 优化,每题附标准口述回答与高频追问,并绑定 Profiler、LeakCanary、MAT 等实战工具。读完可系统复述近 1 年内存方向真高频考点,直接用于面试作答

帅次的博客 977

ICV:Metal Fill Insertion流程

如果不想删除先前加的metal fill,可以使用signoff_create_metal_fill -mode add命令,当使用这个mode后,工具不会fix density问题,哪怕设置了signoff.create_metal_fill.fix_density_errors,设置这个mode后可以使用以下option控制metal fill 的插入。默认情况下,signoff_create_metal_fill是会删掉已经加进去的metal/via fill再重新加的。

m0_61544122的博客 1134

字节跳动凉凉经,一次关于JVM-的面试经历,面试套路深回答需谨慎(泪奔下次我还敢!)(1)

这是因为在 Java 7 中,指定的 PermGen 区大小为 8M,由于 PermGen 中类的元数据信息在每次 FullGC 的时候回收率都偏低,而且为 PermGen 分配多大的空间很难确定,PermSize 的大小依赖于很多因素,比如 JVM 加载的 class 总数、常量池的大小和方法的大小等等。Java 中的运行时数据可以划分为两部分,一部分是线程私有的,包括虚拟机栈、本地方法栈、程序计数器,另一部分是线程共享的,包括方法区和堆。

2401_84006689的博客 828

手把手教你学 MATLAB 实战训练(6.9):卷积神经网络(CNN)模型来进行图像分类任务(预训练的 ResNet-50 模型)

加载数据集数据预处理加载预训练模型修改和扩展预训练模型设置训练选项训练模型测试模型。

xiaoheshang_123的博客 646

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

网易有道安卓二面翻车复盘:无实习非科班如何死磕Handler与Binder源码

在Android开发岗的面试中,Handler消息机制与Binder跨进程通信是绕不开的高频考点。Handler通过Looper死循环与nativePollOnce阻塞-唤醒机制实现线程切换,而Binder借助mmap内存映射完成一次拷贝,从而提升IPC效率。理解这些底层原理,不仅是应对面试连环追问的关键,更是设计健壮缓存、优化应用性能、搭建图片加载库等工程实践的基础。无论是主线程消息调度、进程间通信还是生命周期管理,都离不开对源码级实现逻辑的把握。本文真实复盘一位无实习、非科班的211学生投递网易有道安卓

weixin_33770878的博客 386

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

2024年安卓最新构建自己的Android知识体系,中高级Android大厂高频面试题

我见过很多技术leader在面试的时候,遇到处于迷茫期的大龄程序员,比面试官年龄都大。这些人有一些共同特征:可能工作了7、8年,还是每天重复给业务部门写代码,工作内容的重复性比较高,没有什么技术含量的工作。问到这些人的职业规划时,他们也没有太多想法。其实30岁到40岁是一个人职业发展的黄金阶段,一定要在业务范围内的扩张,技术广度和深度提升上有自己的计划,才有助于在职业发展上有持续的发展路径,而不至于停滞不前。不断奔跑,你就知道学习的意义所在!

2301_77595001的博客 411

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

逃逸分析(Escape Analysis)

一、什么是逃逸     逃逸是指在某个方法之内创建的对象,除了在方法体之内被引用之外,还在方法体之外被其它变量引用到;这样带来的后果是在该方法执行完毕之后,该方法中创建的对象将无法被GC回收,由于其被其它变量引用。正常的方法调用中,方法体中创建的对象将在执行完毕之后,将回收其中创建的对象;故由于无法回收,即成为逃逸。   逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它...

weixin_30955617的博客 727

Java_JVM_逃逸分析技术_栈上分配_标量替换

转自:JVM优化之逃逸分析(Escape Analysis)   什么是逃逸分析(Escape Analysis)? 在编程语言的编译优化原理中,分析指针动态范围的方法称之为逃逸分析。它跟静态代码分析技术中的指针分析和外形分析类似。 通俗一点讲,当一个对象的指针被多个方法或线程引用时,我们称这个指针发生了逃逸。 而用来分析这种逃逸现象的方法,就称之为逃逸分析。 举个例子:   ...

fantaxy025025的专栏 510

jvm中的逃逸分析

文章目录一. 问题背景二. 逃逸分析2.1 什么是逃逸分析?2.2 逃逸分析的jvm参数2.3 一个对象的逃逸状态2.3.1 全局逃逸GlobalEscape2.3.2 参数逃逸ArgEscape2.3.3 没有逃逸2.4 逃逸分析后,有什么作用(好处)?(即逃逸分析优化)2.4.1 锁消除EliminateLocks2.4.2 标量替换Scalar Replacement2.4.3 栈分配三. 总结 一. 问题背景 遇到一个面试题堆中的分区:Eden,survival(from+to),老年代,各自的特点

Android_la的博客 702

java逃逸分析

@author StormMa @date 2017-03-09 生命不息,奋斗不止! 概念引入 我们都知道,Java 创建的对象都是被分配到堆内存上,但是事实并不是这么绝对,通过对Java对象分配的过程分析,可以知道有两个地方会导致Java中创建出来的对象并一定分别在所认为的堆上。这两个点分别是Java中的逃逸分析和TLAB(Thread Local Allocation Buffer

StormMaybin的博客 959

android沙箱逃逸漏洞,【技术分享】沙盒逃逸技术详解(一)

预估稿费:170RMB投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿写在前面的话在过去的十多年里,针对恶意软件沙盒逃逸技术的分析已经成为了对抗高级持续性威胁的银弹,虽然这种技术变得越来越热门,但是恶意软件的开发者们似乎也找到了一种基于静态分析的方法(例如加密、混淆处理和反逆向保护等技术)来躲避传统反病毒安全工具的检测。因此,针对恶意软件沙盒逃逸技术的分析与研究已经成为了我们抵...

weixin_40004051的博客 1188

Android 开发入门实践(二):Kotlin 空安全,从可空类型到安全访问

延续 NewsApp 项目中的新闻标题练习,记录 Kotlin 安全调用。

Easyhood的博客 290

Android 模块化收口:能力组合框架、手写 LifecycleOwner 与热修复共存

二十多个模块业务高度相似却各有差异,核心矛盾在于“统一与灵活”的平衡。通过角色接口组合(如IProjectAware、IVoiceAware)实现能力按需注入,新增功能零侵入;用模板方法锁定生命周期流程,确保顺序一致;结合@CallSuper与工厂方法,既约束关键逻辑,又保留子类自由。最终实现:骨架统一、填空自由,管得住业务,又不侵入业务。

mmsx990的博客 397

Android 开发入门实践(十三):用 Intent、UiState 和 Reducer 理解 MVI 的状态更新

本文记录第141—145课对MVI架构的入门实践,通过“返回列表”功能逐步构建意图(NewsIntent)、状态(NewsUiState)与状态转换(reduce)机制。基于单向数据流,用户操作生成意图,ViewModel通过Reducer根据旧状态计算新状态,并通过StateFlow更新Compose界面。代码实现清晰分离关注点,强化了对状态管理、事件驱动与数据流的理解。

Easyhood的博客 326

Android Message源码阅读

本文梳理了Android Handler机制中Message的核心设计:链表结构、消息复用池(sPool)及回收机制,重点解析了异步消息与同步屏障(SyncBarrier)的协同机制。当主线程需优先处理UI绘制等高优先级任务时,系统通过postSyncBarrier插入屏障,使Looper跳过普通同步消息,优先执行标记为异步的UI渲染任务,确保帧率流畅。该机制是保障Android界面响应性的关键技术之一。

u012116089的博客 201
上一篇: 如何应对Android面试官->JVM内存管理、HSDB大揭秘运行时数据区
下一篇: 如何应对Android面试官->玩转垃圾回收算法, CMS 原理大揭秘
老A说
博客等级 码龄14年 1181粉丝 85原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值