Android 面试题 应用程序结构 十一

news2024/11/25 2:32:46

🔥 Framework主要包含以下模块 🔥

ActivityManagerService  这是一个Activity的管理者,负责管理所有Activity的生命周期。
WindowManagerService  它是手机屏幕的的管理者,管理着屏幕的详细情况,所有对屏幕的操作最终都是通过它,控制着屏幕的显示、隐藏和层次处理。
ComtentProvider  内容提供者,给Android提供了一个应用访问另一个应用的数据的能力。
ViewSystem  系统试图,包括列表,网格,文本和按钮的测量、排列、绘制。 Notification
Manager  通知管理者,负责通知的管理。
PackageMangerService  包管理者,包信息的管理。
Telephoney Manager  通信管理者
Resoure Manager  资源管理者
Location Manager  位置管理者
Xmpp Manager  推送管理者

🔥 FrameWork三大核心 🔥 

View.java  负责布局的排列,绘制,测量和事件分发,按键事件。

ActivityManagerService.java  管理所有应用程序的Activity等

WindowManagerService.java  给所有应用程序分配窗口,并管理这些窗口。

🔥 选择Binder的原因 🔥 

 Android是基于Linux内核的,所以Android要实现进程间的通信,其实大可使用linux原有的一些手段,比如管道,共享内存,socket等方式,但是Android还是采用了Binder作为主要机制,说明Binder具有无可比拟的优势。

其实进程通信大概就两个方面因素,一者性能方面,传输效率问题,传统的管道队列模式采用内存缓冲区的方式,数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程,而socket都知道传输效率低,开销大,用于跨网络进程交互比较多,共享内存虽然无需拷贝。

二者这是安全问题,Android作为一个开放式,拥有众多开发者的的平台,应用程序的来源广泛,确保终端安全是非常重要的,传统的IPC通信方式没有任何措施,基本依靠上层协议,其一无法确认对方可靠的身份,Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志,传统的IPC要发送类似的UID也只能放在数据包里,但也容易被拦截,恶意进攻,socket则需要暴露自己的ip和端口,知道这些恶意程序则可以进行任意接入。

 综上所述,Android需要一种高效率,安全性高的进程通信方式,也就是Binder,Binder只需要一次拷贝,性能仅次于共享内存,而且采用的传统的C/S结构,稳定性也是没得说,发送添加UID/PID,安全性高。

🔥 Binder实现机制 进程隔离 🔥

 我们知道进程之间是无法直接进行交互的,每个进程独享自己的数据,而且操作系统为了保证自身的安全稳定性,将系统内核空间和用户空间分离开来,保证用户程序进程崩溃时不会影响到整个系统,简单的说就是,内核空间(Kernel)是系统内核运行的空间,用户空间(UserSpace)是用户程序运行的空间。为了保证安全性,它们之间是隔离的,所以用户空间的进程要进行交互需要通过内核空间来驱动整个过程。

 🔥 Binder实现机制 C/S结构 🔥

Binder是基于C/S机制的,要实现这样的机制,server必须需要有特定的节点来接受到client的请求,也就是入口地址,像输入一个网址,通过DNS解析出对应的ip,然后进行访问,这个就是server提供出来的节点地址,而Binder而言的话,与传统的C/S不太一样,Binder本身来作为Server中提供的节点,client拿到Binder实体对象对应的地址去访问Server,对于client而言,怎么拿到这个地址并建立起整个通道是整个交互的关键所在,而且Binder作为一个Server中的实体,对象提供一系列的方法来实现服务端和客户端之间的请求,只要client拿到这个引用就可以或者一个有着该方法代理对象的引用,就可以进行通信了。

 面向对象思想的引入将进程间通信转化为通过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中。最诱人的是,这个引用和java里引用一样既可以是强类型,也可以是弱类型,而且可以从一个进程传给其它进程,让大家都能访问同一Server,就象将一个对象或引用赋值给另一个引用一样。Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行于同一个面向对象的程序之中。形形色色的Binder对象以及星罗棋布的引用仿佛粘接各个应用程序的胶水,这也是Binder在英文里的原意。

🔥 Binder实现机制 通信模型 🔥

Binder基于C/S的结构下,定义了4个角色:Server、Client、ServerManager、Binder驱动,其中前三者是在用户空间的,也就是彼此之间无法直接进行交互,Binder驱动是属于内核空间的,属于整个通信的核心,虽然叫驱动,但是实际上和硬件没有太大关系,只是实现的方式和驱动差不多,驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。 

🔥  ServerManager的作用 🔥 

我们知道ServerManager也是属于用户空间的一个进程,主要作用就是作为Server和client的桥梁,client可以ServerManager拿到Server中Binder实体的引用,这么说可能有点模糊,举个简单的例子,我们访问,www.baidu.com,百度首页页面就显示出来了,首先我们知道,这个页面肯定是发布在百度某个服务器上的,DNS通过你这个地址,解析出对应的ip地址,再去访问对应的页面,然后再把数据返回给客户端,完成交互。这个和Binder的C/S非常类似,这里的DNS就是对应的ServerManager,首先,Server中的Binder实体对象,将自己的引用(也就是ip地址)注册到ServerManager,client通过特定的key(也就是百度这个网址)和这个引用进行绑定,ServerManager内部自己维护一个类似MAP的表来一一对应,通过这个key就可以向ServerManager拿到Server中Binder的引用,对应到Android开发中,我们知道很多系统服务都是通过Binder去和AMS进行交互的,比如获取音量服务:

AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);

细心的朋友应该发现ServerManager和Server也是两个不同的进程呀,Server要向ServerManager去注册不是也要涉及到进程间的通信吗,当前实现进程间通信又要用到进程间的通信,你这不是扯犊子吗....莫急莫急,Binder的巧妙之处在于,当ServerManager作为Serve端的时候,它提供的Binder比较特殊,它没有名字也不需要注册,当一个进程使用BINDER_SET_CONTEXT_MGR命令将自己注册成SMgr时Binder驱动会自动为它创建Binder实体,这个Binder的引用在所有Client中都固定为0而无须通过其它手段获得。也就是说,一个Server若要向ServerManager注册自己Binder就必需通过0这个引用号和ServerManager的Binder通信,有朋友又要问了,server和client属于两个不同的进程,client怎么能拿到server中对象,不妨先看看下面的交互图

🔥 Binder实现机制  角色的定位 🔥

Binder本质上只是提供了一种通信的方式,和我们具体要实现的内容没有关系,为了实现这个服务,我们需要定义一些接口,让client能够远程调用服务,因为是跨进程,这时候就要设计到代理模式,以接口函数位基准,client和server去实现接口函数,Server是服务真正的实现,client作为一个远程的调用。

  • 从Server进程来看,Binder是存在的实体对象,client通过transact()函数,经过Binder驱动,最终回调到Binder实体的onTransact()函数中。
  • 从 Client进程的角度看,Binder 指的是对 Binder 代理对象,是 Binder 实体对象的一个远程代理,通过Binder驱动进行交互

🔥 Handler 简介🔥 

Handler是Android消息机制的上层接口。通过它可以轻松地将一个任务切换到Handler所在的线程中去执行。通常情况下,Handler的使用场景就是 更新UI

🔥 Handler的使用 🔥 

在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI。

 

public class Activity extends android.app.Activity {
    private Handler mHandler = new Handler(){
        @Override        
                public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 更新UI
        }
    }
    ;
    @Override    
    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override            
                         public void run() {
                // 执行耗时任务                ...                
                // 任务执行完后,通知Handler更新UI                
                Message message = Message.obtain();
                message.what = 1;
                mHandler.sendMessage(message);
            }
        }
        ).start();
    }
}

🔥 Handler架构 🔥 

Handler消息机制主要包括: MessageQueue、 Handler、 Looper这三大部分,以及 Message。 

  • Message:需要传递的消息,可以传递数据;
  • MessageQueue:消息队列,但是它的内部实现并不是用的队列,而是通过单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能是向消息池投递消息( MessageQueue.enqueueMessage)和取走消息池的消息( MessageQueue.next)。
  • Handler:消息辅助类,主要功能是向消息池发送各种消息事件( Handler.sendMessage)和处理相应消息事件( Handler.handleMessage);
  • Looper:消息控制器,不断循环执行( Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。

 

 

从上面的类图可以看出: 

  • Looper有一个MessageQueue消息队列;
  • MessageQueue有一组待处理的Message;
  • Message中记录发送和处理消息的Handler;
  • Handler中有Looper和MessageQueue。

🔥 MessageQueue、Handler和Looper三者之间的关系 🔥 

 每个线程中只能存在一个Looper,Looper是保存在ThreadLocal中的。 主线程(UI线程)已经创建了一个Looper,所以在主线程中不需要再创建Looper,但是在其他线程中需要创建Looper。 每个线程中可以有多个Handler,即一个Looper可以处理来自多个Handler的消息。 Looper中维护一个MessageQueue,来维护消息队列,消息队列中的Message可以来自不同的Handler。

 

 🔥 Handler的运行流程 🔥

在子线程执行完耗时操作,当Handler发送消息时,将会调用 MessageQueue.enqueueMessage,向消息队列中添加消息。 当通过 Looper.loop开启循环后,会不断地从消息池中读取消息,即调用 MessageQueue.next, 然后调用目标Handler(即发送该消息的Handler)的 dispatchMessage方法传递消息, 然后返回到Handler所在线程,目标Handler收到消息,调用 handleMessage方法,接收消息,处理消息。

 

🔥 源码分析 在子线程创建Handler 🔥

class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare();
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        }
        ;
        Looper.loop();
    }
}

从上面可以看出,在子线程中创建Handler之前,要调用 Looper.prepare()方法,Handler创建后,还要调用 Looper.loop()方法。而前面我们在主线程创建Handler却不要这两个步骤,因为系统帮我们做了。

🔥 源码分析 主线程的Looper 🔥 

 在ActivityThread的main方法,会调用 Looper.prepareMainLooper()来初始化Looper,并调用 Looper.loop()方法来开启循环。

public final class ActivityThread extends ClientTransactionHandler {
    // ...    
    public static void main(String[] args) {
        // ...        
        Looper.prepareMainLooper();
        // ...        
        Looper.loop();
    }
}

🔥  源码分析 Looper 🔥

从上可知,要使用Handler,必须先创建一个Looper。

初始化Looper:

 

public final class Looper {
    public static void prepare() {
        prepare(true);
    }
    private static void prepare(Boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    private Looper(Boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    // ...
}

从上可以看出,不能重复创建Looper,每个线程只能创建一个。创建Looper,并保存在 ThreadLocal。其中ThreadLocal是线程本地存储区(Thread Local Storage,简称TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。

🔥 源码分析 开启Looper 🔥

public final class Looper {
    // ...    
    public static void loop() {
        // 获取TLS存储的Looper对象        
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        // 进入loop主循环方法        
        for (;;) {
            Message msg = queue.next();
            // 可能会阻塞,因为next()方法可能会无线循环            
            if (msg == null) {
                // No message indicates that the message queue is quitting.                
                return;
            }
            // This must be in a local variable, in case a UI event sets the logger            
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +                        
                                                msg.callback + ": " + msg.what);
            }
            // ...            
            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                // 获取msg的目标Handler,然后分发Message                
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            }
            finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            // ...            
            msg.recycleUnchecked();
        }
    }
}

 🔥 源码分析 创建Handler 🔥

public class Handler {
    // ...    
    public Handler() {
        this(null, false);
    }
    public Handler(Callback callback, Boolean async) {
        // ...        
        // 必须先执行Looper.prepare(),才能获取Looper对象,否则为null        
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(                
                  "Can't create handler inside thread " + Thread.currentThread()                        
                  + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        // 消息队列,来自Looper对象        
        mCallback = callback;
        // 回调方法        
        mAsynchronous = async;
        // 设置消息是否为异步处理方式
    }
}

🔥 源码分析 发送消息 🔥

子线程通过Handler的post()方法或send()方法发送消息,最终都是调用 sendMessageAtTime()方法。

post 方法

 

public final Boolean post(Runnable r){
    return sendMessageDelayed(getPostMessage(r), 0);
}
public final Boolean postAtTime(Runnable r, long uptimeMillis){
    return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final Boolean postAtTime(Runnable r, Object token, long uptimeMillis){
    return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final Boolean postDelayed(Runnable r, long delayMillis){
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

 send方法

public final Boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}
public final Boolean sendEmptyMessage(int what){
    return sendEmptyMessageDelayed(what, 0);
}
public final Boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}
public final Boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageAtTime(msg, uptimeMillis);
}
public final Boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
sendMessageAtTime()
public Boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(            
                  this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
private Boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

🔥 源码分析 分发消息 🔥 

 在loop()方法中,获取到下一条消息后,执行 msg.target.dispatchMessage(msg),来分发消息到目标Handler。

public class Handler {
    // ...    
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            // 当Message存在回调方法,调用该回调方法            
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                // 当Handler存在Callback成员变量时,回调其handleMessage()方法                
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // Handler自身的回调方法            
            handleMessage(msg);
        }
    }
    private static void handleCallback(Message message) {
        message.callback.run();
    }
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/819915.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatGPT学python——制作自己的AI模型(一)初步了解

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★前端炫酷代码分享 ★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ 解决算法&#xff0c;一个专栏就够了★ ★ 架…

2023国际先进制造青年科学家大会圆满结束!易知微受邀进行数字孪生主题报告分享

2023年7月30日&#xff0c;以“新青年&#xff0c;新视野&#xff0c;新制造”为主题的2023国际先进制造青年科学家大会在浙江绍兴开幕&#xff0c;旨在强力推进创新深化改革攻坚开放提升&#xff0c;进一步做强“人才&#xff0b;产业”双联驱动、人才集聚与产业发展共振同兴&…

NLP/CV相关模型库-阿里

模型库地址&#xff1a;https://www.modelscope.cn/models

30岁转行学编程,更容易成功

“男人30而立&#xff0c;我没有退路&#xff0c;只有选择勇往直前&#xff0c;这样我才能无所畏惧&#xff0c;因为我希望在我的人生篇章里&#xff0c;能拥有浓墨重彩的一笔&#xff01;” 30岁&#xff0c;在经历一番“打击”后毅然决然选择学编程&#xff0c;为家人也为自…

解决Mysql报错2003 (HY000): Can‘t connect to MySQL server on ‘localhost‘ (10061)

1.找到mysql文件夹&#xff0c;将my,ini文件放入bin文件夹 2.管理员模式打开cmd 3.输入netstat -ano查看端口占用情况&#xff0c;这里我已经开启mysql应用&#xff0c;所以会有3306&#xff0c;如果没有开启是不会有的 4.输入sc delete mysql&#xff0c;删除mysql服务 5.将…

Vue如何实现重定向

在刚登录之后&#xff0c;我们想要默认展示一下用户登录&#xff0c;这个功能如何实现&#xff1a; 就是重定向为/home/user这个值 我们就是在/home 上加一个重定向 具体怎么做那&#xff0c;先找到/home的路由规则 想要实现重定向,增加一个redirect就行

基于SpringBoot+Vue的广场舞团管理系统设计与实现(源码+LW+部署文档等)

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

无涯教程-Lua - 环境安装

在Windows上安装 为Windows环境开发了一个单独的名为" SciTE"的IDE,可以从https://code.google.com/p/luaforwindows/下载部分。 运行下载的可执行文件以安装Lua IDE。 由于它是一个IDE&#xff0c;因此您可以使用它来创建和构建Lua代码。 如果您有兴趣在命令行模…

【私藏】matlab踏实手把手常规练习附全套学习资料

目录 前言 福利&#xff1a;文末有matlab全套资料哦 01分段函数 02复合图 ⚪️ 两个一元函数yx^3-x-1和y|x|^(0.2)sin(5x)在区间-1<2的复合图。函数与图像<> 03三维平面图 ⚪️ 三维平面图使用mesh绘制网格图&#xff0c;surf绘制三维曲面图&#xff0c;使用…

filebeat kibana elasticsearch 日志监控

解压三个压缩包 一、filebeat的安装部署 1、打开filebeat的配置文件 2、Filebeat inputs 处打开日志输入开关&#xff0c;设置要监控的路径 3、Outputs 输出中设置Elasticsearch output的输出地址 4、执行 ./filebeat & 二、Elasticsearch 安装部署 1、修改配置文件 …

C语言:相交链表

Lei宝啊&#xff1a;个人主页 愿美好与我们不期而遇 题目&#xff1a; 描述 给你两个单链表的头节点 headA和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 接口 struct ListNode *getIntersectionNode (str…

使用javax.validation.constraints进行数据验证

使用javax.validation.constraints进行数据验证 在Java应用中&#xff0c;数据的验证是一个很重要的部分&#xff0c;特别是在接收用户输入或处理外部数据时。为了简化和标准化数据验证的过程&#xff0c;Java提供了javax.validation.constraints包&#xff0c;其中包含一系列注…

MySQL数据库——约束

文章目录 前言主键约束创建主键单列主键联合主键通过修改表结构来指定主键 删除主键约束 自增主键约束创建自增主键 非空约束创建非空约束删除非空约束 唯一约束创建唯一约束删除唯一性约束 默认约束创建默认约束删除默认约束&#xff08;改为null为默认值&#xff09; 零填充约…

安装Vue浏览器插件vue.Devtools(小白无压力教程)

1、打开网址:选择vue.js.Devtoolshttps://chrome.zzzmh.cn/index#/search极简插件是一个优质Chrome插件扩展收录下载网站&#xff0c;收录热门好用的Chrome插件扩展&#xff0c;国内最方便的插件下载网站。https://chrome.zzzmh.cn/index#/search 2、下载安装包后&#xff0c;…

【论文简述】DIP: Deep Inverse Patchmatch for High-Resolution Optical Flow(CVPR 2022)

一、论文简述 1. 第一作者&#xff1a;Rui Li 2. 发表年份&#xff1a;2023 3. 发表期刊&#xff1a;CVPR 4. 关键词&#xff1a;光流、深度学习、PatchMatch、局部搜索 5. 探索动机&#xff1a;对于深度学习来说&#xff0c;除了准确性之外&#xff0c;性能和内存也是一个…

基于 FFlogs API 快速实现的 logs 颜色查询小爬虫

文章目录 找到接口解析响应需要平均颜色和过本次数&#xff1f; 找到接口 首先试了一下爬虫&#xff0c;发现和wow一样官网上有暴露的 API&#xff0c;链接在&#xff1a;FFlogs v1 API 文档链接 通过查询官方提供的 API 接口得知&#xff1a; user_name 角色名字 api_key …

解决安装VMware Tools灰色问题,解决重新安装VMware Tools灰色问题

一、环境 vmware 版本&#xff1a;17.0.0 build-20800274 centos 9 二、问题 安装VMware Tools 按钮为灰色 三、解决方法 网上很多写这些如何解决该问题的&#xff0c;如&#xff1a;配置CD/DVD&#xff08;SATA&#xff09;为自动检测&#xff0c;或者指定其CD镜像文件为…

3个命令定位CPU飙高

top 指令找出消耗CPU最厉害的那个进程的pid top -H -p 进程pid 找出耗用CPU资源最多的线程pid printf ‘0x%x\n’ 线程pid 将线程pid转换为16进制 结合jstack 找出哪个代码有问题 jstack 进程pid | grep 16进制的线程pid -A 多少行日志 jstack 进程pid | grep 16进制的线程…

DP-GAN-判别器代码

将输出的rgb作为输入&#xff0c;输入到判别器中。接着执行一个for循环&#xff0c;看一下body_down列表的组成和x经过body_down之后的值。 body_down是由残差块D组成的列表&#xff1a; 残差块的参数为&#xff1a;(3,128),(128,128),(128,256),(256,256),(256,512),(512,5…

虹科分享 | 关于内存取证你应该知道的那些事

什么是内存取证&#xff1f; 内存取证是指在计算机或其他数字设备运行时&#xff0c;通过对其随时存储的内存数据进行采集、分析和提取&#xff0c;以获取有关设备状态、操作过程和可能存在的安全事件的信息。内存取证是数字取证的一个重要分支&#xff0c;用于从计算机的RAM&…