Java篇 - 四种引用(Reference)实战

2023-05-16

 

Java的垃圾回收(GC)是虚拟机自动管理的,前面我有篇文章专门讲GC:《JVM篇 - GC给你整明白》

Java内存管理分为内存分配和内存回收,都不需要程序员负责,垃圾回收的机制主要是看对象是否有引用指向该对象。

Java对象的引用包括:强引用,软引用,弱引用,虚引用,Java中提供这四种引用类型主要有两个目的:

  • 可以让程序员通过代码的方式决定某些对象的生命周期。
  • 有利于JVM进行垃圾回收。

 

目录:

  1. 强引用(StrongReference)
  2. 软引用(SoftReference)
  3. 弱引用(WeakReference)
  4. 虚引用(PhantomReference)
  5. 引用队列(ReferenceQueue)

 

 

1. 强引用(StrongReference)

 

  • 1.1 概念

强引用是指创建一个对象并它赋值给一个引用,引用是存在JVM中的栈中的。

例如:

    private static void testStrongReference() {
        Object object = new Object();
        Object[] objects = new Object[1000];
    }

可以看到,testStrongReference()方法中,创建了一个对象和一个对象数组,当testStrongReference()执行完,JVM会自动回收它们(属于局部变量)。但是因为它们都是强引用,所以当执行到testStrongRefenerce()时,如果内存不足,不足够在堆中分配这些内存,那么JVM将抛出OutOfMemory,因为强引用的对象,只要有引用变量指向它们的时候,它们将不会被垃圾回收。

如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。

 

  • 1.2 例子

看看Vector类的清理方法:

    protected Object[] elementData;
    
    public synchronized void removeAllElements() {
        modCount++;
        // Let gc do its work
        for (int i = 0; i < elementCount; i++)
            elementData[i] = null;

        elementCount = 0;
    }

在清除数据的时候,将数组中的每个元素都置为null,中断强引用与对象之间的关系,让GC的时候能够回收这些对象的内存。

 

 

 

2. 软引用(SoftReference)

我做Android时,第一次接触引用这个概念就是从软引用开始的,那是13年的时候,当时还没有那么多的第三方图片加载框架,图片加载都得自己写。当时的实现方法中,内存中缓存的Bitmap使用的就是软引用。

 

  • 2.1 概念

如果一个对象具有软引用,只要内存空间足够,垃圾回收器就不会回收它。如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。

软引用可用来实现内存敏感的高速缓存,比如网页缓存、图片缓存等。使用软引用能防止内存泄露,增强程序的健壮性。   
SoftReference的特点是它的一个实例保存对一个Java对象的软引用, 该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。一旦垃圾线程回收该Java对象之 后,get()方法将返回null。

 

  • 2.2 例子

分享一个平时开发中的例子,就是上面说的图片缓存。

public class ImageMemoryCache {
	/**
	 * 从内存读取数据速度是最快的,为了更大限度使用内存,这里使用了两层缓存。 硬引用缓存不会轻易被回收,用来保存常用数据,不常用的转入软引用缓存。
	 */
	private static final int SOFT_CACHE_SIZE = 20; // 软引用缓存容量
	private static LruCache<String, Bitmap> mLruCache; // 硬引用缓存
	private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache; // 软引用缓存

	public ImageMemoryCache(Context context) {
		int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
		int cacheSize = 1024 * 1024 * memClass / 4; // 硬引用缓存容量,为系统可用内存的1/4
		mLruCache = new LruCache<String, Bitmap>(cacheSize) {
			@Override
			protected int sizeOf(String key, Bitmap value) {
				if (value != null)
					return value.getRowBytes() * value.getHeight();
				else
					return 0;
			}

			@Override
			protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
				if (oldValue != null)
					// 硬引用缓存容量满的时候,会根据LRU算法把最近没有被使用的图片转入此软引用缓存
					mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));
			}
		};
		mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_SIZE, 0.75f, true) {
			private static final long serialVersionUID = 6040103833179403725L;

			@Override
			protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {
				if (size() > SOFT_CACHE_SIZE) {
					return true;
				}
				return false;
			}
		};
	}

	/**
	 * 从缓存中获取图片
	 */
	public Bitmap getBitmapFromCache(String url) {
		Bitmap bitmap;
		// 先从硬引用缓存中获取
		synchronized (mLruCache) {
			bitmap = mLruCache.get(url);
			if (bitmap != null) {
				// 如果找到的话,把元素移到LinkedHashMap的最前面,从而保证在LRU算法中是最后被删除
				mLruCache.remove(url);
				mLruCache.put(url, bitmap);
				return bitmap;
			}
		}
		// 如果硬引用缓存中找不到,到软引用缓存中找
		synchronized (mSoftCache) {
			SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);
			if (bitmapReference != null) {
				bitmap = bitmapReference.get();
				if (bitmap != null) {
					// 将图片移回硬缓存
					mLruCache.put(url, bitmap);
					mSoftCache.remove(url);
					return bitmap;
				} else {
					mSoftCache.remove(url);
				}
			}
		}
		return null;
	}

	/**
	 * 添加图片到缓存
	 */
	public void addBitmapToCache(String url, Bitmap bitmap) {
		if (bitmap != null) {
			synchronized (mLruCache) {
				mLruCache.put(url, bitmap);
			}
		}
	}
	
	public void removeBitmap(String url) {
		if (url != null) {
			synchronized (mLruCache) {
				mLruCache.remove(url);
			}
			synchronized (mSoftCache) {
				mSoftCache.remove(url);
			}
		}
	}

	public void clearCache() {
		mSoftCache.clear();
	}
}

 

 

 

3. 弱引用(WeakReference)

 

  • 3.1 概念

弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。其实,上面图片缓存的框架用弱引用也可以做,特别是在移动设备上,内存容量有限。

 

  • 3.2 例子

还是以平时开发中遇到的问题为例:

(1) Handler内存泄漏问题

Android中使用Handler可以实现在不同线程间通信,如果在Activity界面使用Handler,由于Handler的机制(这边就不细说),当Activity退出后,处理不当,Handler会一直持有该Activity的引用,那引起的内存泄漏是很大的。如何解决呢?

private static class NoLeakHandler extends Handler{

        private WeakReference<Activity> mActivityRef;

        public NoLeakHandler(Activity activity){
            mActivityRef = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (mActivityRef.get() != null) {
                 // 处理相应的逻辑
            }
        }
    }
}

使用静态内部类(脱离外部类引用的限制) + 弱引用的方式解决这种内存泄漏的问题。当然解决源头还是在Activity退出的时候,移除Handler内部消息队列的数据。

 

(2) ViewPager适配器

Android中的ViewPager + Fragment是很多项目界面采用的组合,但是Fragment用的不好,性能上会相差很大,造成内存占用大,卡顿的问题。我一般会这么写ViewPager的适配器:

    private static final class TabPagerAdapter extends FragmentPagerAdapter {

        private final WeakReference<Fragment>[] data;

        @SuppressWarnings("unchecked")
        TabPagerAdapter(FragmentManager fm) {
            super(fm);
            data = (WeakReference<Fragment>[]) Array.newInstance(WeakReference.class, getCount());
        }

        @Override
        public Fragment getItem(int position) {
            if (position < 0 || position >= getCount()) {
                return new Fragment();
            }
            Fragment f = null;
            if (!Requires.isNull(data[position])) {
                f = data[position].get();
            } else {
                if (position == 0) {
                    f = HomeFragment.newInstance();
                } else if (position == 1) {
                    f = DappFragment.newInstance();
                } else if (position == 2) {
                    f = SettingsFragment.newInstance();
                }
                data[position] = new WeakReference<>(f);
            }
            return f;
        }

        @Override
        public int getCount() {
            return 3;
        }
    }

第一是懒加载方式,滑动到某个界面的时候,再构造对应的Fragment,同时Fragment使用弱引用包装。

 

 

 

4. 虚引用(PhantomReference)

 

4.1 概念

虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。

要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

 

4.2 例子

    public static void main(String[] args) {
        Object object = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        PhantomReference<Object> phantomReference = new PhantomReference<>(object, referenceQueue);
        // 一直返回null,PhantomReference的get()结果必定为null
        System.out.println("phantomReference.get() = " + phantomReference.get());
        Reference<?> reference;
        while ((reference = referenceQueue.poll()) != null) {
            if (reference.equals(phantomReference)) {
                System.out.println("被回收了");
            }
        }
    }

注意:PhantomReference的get()结果必定为null。

 

 

 

5. 引用队列(ReferenceQueue)

 

  • 5.1 概念

上面在虚引用中说到了引用队列,再举个例子,作为一个Java对象,SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。所以,当软可及对象被回收之后,虽然这个SoftReference对象的get()方法返回null,但这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。在java.lang.ref包里还提供了ReferenceQueue。如果在创建SoftReference对象的时候,使用了一个ReferenceQueue对象作为参数提供给SoftReference的构造方法:

ReferenceQueue queue = new ReferenceQueue();  
SoftReference  ref = new SoftReference(object, queue);  

那么当这个SoftReference所软引用的对象被垃圾收集器回收的同时,ref所强引用的SoftReference对象被列入ReferenceQueue。也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。另外从ReferenceQueue这个名字也可以看出,它是一个队列,当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。

在任何时候,我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收,于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。

SoftReference ref = null;
while ((ref = (EmployeeRef) q.poll()) != null) {
    // 清除ref
}

再来看看几种引用的源码定义:

public class SoftReference<T> extends Reference<T> {

    static private long clock;

    private long timestamp;

    public SoftReference(T referent) {
        super(referent);
        this.timestamp = clock;
    }

    public SoftReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
        this.timestamp = clock;
    }

    public T get() {
        T o = super.get();
        if (o != null && this.timestamp != clock)
            this.timestamp = clock;
        return o;
    }
}
public class WeakReference<T> extends Reference<T> {

    public WeakReference(T referent) {
        super(referent);
    }

    public WeakReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
}
public class PhantomReference<T> extends Reference<T> {
        
    // get()只会返回null
    public T get() {
        return null;
    }

    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
}

可以看到,几种引用都提供了传入引用队列的构造器。

 

  • Reference类
public abstract class Reference<T> {

    private T referent;

    volatile ReferenceQueue<? super T> queue;

    @SuppressWarnings("rawtypes")
    Reference next;

    transient private Reference<T> discovered; 

    static private class Lock { }
    private static Lock lock = new Lock();

    private static Reference<Object> pending = null;

    private static class ReferenceHandler extends Thread {

        private static void ensureClassInitialized(Class<?> clazz) {
            try {
                Class.forName(clazz.getName(), true, clazz.getClassLoader());
            } catch (ClassNotFoundException e) {
                throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
            }
        }

        static {
            ensureClassInitialized(InterruptedException.class);
            ensureClassInitialized(Cleaner.class);
        }

        ReferenceHandler(ThreadGroup g, String name) {
            super(g, name);
        }

        public void run() {
            while (true) {
                tryHandlePending(true);
            }
        }
    }

    static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) {
                    r = pending;
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    pending = r.discovered;
                    r.discovered = null;
                } else {
                    if (waitForNotify) {
                        lock.wait();
                    }
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            Thread.yield();
            return true;
        } catch (InterruptedException x) {
            return true;
        }

        if (c != null) {
            c.clean();
            return true;
        }

        ReferenceQueue<? super Object> q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        return true;
    }

    static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        Thread handler = new ReferenceHandler(tg, "Reference Handler");
        handler.setPriority(Thread.MAX_PRIORITY);
        handler.setDaemon(true);
        handler.start();

        SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
            @Override
            public boolean tryHandlePendingReference() {
                return tryHandlePending(false);
            }
        });
    }

    public T get() {
        return this.referent;
    }

    public void clear() {
        this.referent = null;
    }

    public boolean isEnqueued() {
        return (this.queue == ReferenceQueue.ENQUEUED);
    }

    public boolean enqueue() {
        return this.queue.enqueue(this);
    }

    Reference(T referent) {
        this(referent, null);
    }

    Reference(T referent, ReferenceQueue<? super T> queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }
}

大致解释下:

Reference主要是负责内存的一个状态,当然它还和java虚拟机,垃圾回收器打交道。Reference类首先把内存分为4种状态Active,Pending,Enqueued,Inactive。

  • 一般来说内存一开始被分配的状态都是Active。
  • Pending是指快要被放进队列的对象,也就是马上要回收的对象。
  • Enqueued就是对象的内存已经被回收了,我们已经把这个对象放入到一个队列中,方便以后我们查询某个对象是否被回收。
  • Inactive就是最终的状态,不能再变为其它状态。

几个变量:

  • referent表示其引用的对象,即在构造的时候需要被包装在其中的对象。
  • queue是对象即将被回收时所要通知的队列。当对象即将被回收时,整个reference对象,而不仅仅是被回收的对象,会被放到queue里面,然后外部程序即可通过监控这个queue即可拿到相应的数据了。
  • next即当前引用节点所存储的下一个即将被处理的节点。但next仅在放到queue中才会有意义,因为只有在enqueue的时候,会将next设置为下一个要处理的Reference对象。为了描述相应的状态值,在放到队列当中后,其queue就不会再引用这个队列了。而是引用一个特殊的 ENQUEUED(内部定义的一个空队列)。因为已经放到队列当中,并且不会再次放到队列当中。
  • discovered表示要处理的对象的下一个对象。即可以理解要处理的对象也是一个链表,通过discovered进行排队,这边只需要不停地拿到pending,然后再通过discovered不断地拿到下一个对象赋值给pending即可,直到取到了最有一个。它是被JVM使用的。
  • pending是等待被入队的引用列表。JVM收集器会添加引用到这个列表,直到Reference-handler线程移除了它们。这个列表使用discovered 字段来连接它下一个元素(即 pending 的下一个元素就是discovered对象。r = pending; pending = r.discovered)。

当 Refrence类被加载的时候,会执行静态代码块。在静态代码块里面,会启动ReferenceHandler线程,并设置线程的级别为最大级别:Thread.MAX_PRIORITY。

在 tryHandlePending()方法里面,检查 pending是否为null,如果pending不为null,则将pending进行enqueue,否则线程进入 wait状态。简单来说,垃圾回收器会把 References添加进入,Reference-handler thread会移除它,即discovered和pending 是由垃圾回收器进行赋值的。

 

总结一下:

Refrence和引用队列ReferenceQueue联合使用时,如果Refrence持有的对象被垃圾回收,Java虚拟机就会把这个引用加入到与之关联的引用队列中。Android中著名的内存泄漏检测工具LeakCannary就是用这种方式做的,后面的Android第三方库源码分析会有一篇LeakCannary的源码分析。


 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java篇 - 四种引用(Reference)实战 的相关文章

  • Git Commit提交规范总结

    文章目录 前言git commit 提交规范提交消息头 commit message header 提交消息具体内容 commit message body 提交消息尾述 commit message footer Revert 表情 Em
  • 常用kubectl命令总结

    文章目录 配置kubeconfig帮助信息命令查看具体某一个命令的帮助信息列出全局的选项参数 xff08 适用所有的命令 xff09 显示合并的 kubeconfig 配置或一个指定的 kubeconfig 文件 基本命令罗列所支持的完整资
  • 解决:org.apache.catalina.connector.ClientAbortException: java.io.IOException: 断开的管道

    文章目录 项目场景问题描述原因分析解决方案 项目场景 jdk11 Spring Boot 2 x 项目 xff0c Tomcat容器 Nginx 问题描述 系统日志中 xff0c 时不时会出现下面的异常信息 xff1a org apache
  • 解决:No converter for [xxxx] with preset Content-Type ‘text/plain;version=0.0.4;charset=utf-8‘

    文章目录 项目背景问题描述问题分析解决方案方案一 xff1a 修改Controller定义方案二 xff1a 修改Controller返回值方案三 xff1a 全局处理 项目背景 Spring Boot 2 X 问题描述 错误信息如下 xf
  • SYD8821 串口模块使用说明【串口0中断要屏蔽底层调用】

    SYD8821是具有全球领先低功耗 RX 2 4mA 64 94 5dBm灵敏度 xff0c TX 4 3mA 64 0dBm输出功率 的蓝牙低功耗SOC芯片 xff0c 在极低电流下实现了优异的射频性能 xff0c 搭配176kB SRA
  • MySQL的information_schema库下的常用sql

    文章目录 information schema TABLES查看该数据库实例下所有库大小 MB为单位 查看该实例下各个库大小 MB为单位 查看表大小 MB为单位 熟练使用 information schema库里的表 显示在库里的表 xff
  • shell脚本批量转文件格式:dos2unix

    文章目录 可以使用shell脚本实现 xff1a span class token shebang important bin sh span span class token assign left variable dir span s
  • 解决:com.atomikos.icatch.SysException: Error in init: Log already in use? tmlog in ./

    文章目录 项目场景问题描述原因分析详细分析 解决方案 项目场景 Spring Boot 2 x xff0c 集成 atomikos 问题描述 今天在同一个环境启动两个项目时报错 xff0c 因为两个项目同时涉及到分布式事物和切换数据源相关
  • Nginx日志介绍

    文章目录 access log日志流量统计 access log 日志文件一般存放在 var log nginx 下 xff0c 可以使用 tail f命令查看access日志 span class token function tail
  • JVM (Micrometer)-4701面板参数介绍

    文章目录 Quick Facts 概览 堆和非堆内存有以下几个概念 I O Overview xff08 服务黄金指标 xff09 JVM Memory xff08 JVM内存 xff09 JVM Misc xff08 JVM负载 xff0
  • curl文件传输命令

    CURL curl transfer a URL curl 是一个利用URL语法在命令行下工作的文件传输工具 支持文件上传和下载 格式 curl options URLs URL xff1a 通过大括号指定多个url 示例 xff1a cu
  • RS-485信号解析

    这次来看看RS 485信号 使用绿联的USB转RS485模块 线用的颜色不对 xff0c 类型也不对 xff0c 实际使用中请用带屏蔽层的双绞线 示波器CH1是R xff08 B xff09 示波器CH2是R 43 xff08 A xff0
  • T t与T t = T()的区别

    主要的区别就是默认构造函数对内置类型的初始化上 如果没有T中没有定义构造函数 xff0c 则对于 T t xff0c 并不会对 t 中内置类型设置初始值 xff0c 是一个随机值 但对于 T t 61 T xff0c 对 t 中内置类型会设
  • Effective STL:杂记(一)

    1 避免使用vector lt bool gt vector lt bool gt 实际上并不能算是一个STL容器 xff0c 实际上也并不存储bool 因为一个对象要成为STL容器 xff0c 就必须满足C 43 43 标准的第23 1节
  • 限制长度双向链表的插入操作

    面试遇到的问题 xff0c 一开始面试官是问我有什么方案可以实现排行榜 xff0c 当时给出了两个方案 后面面试官又在我的其中一种方案上让我手写代码实现排序双线链表的插入 xff0c 根据score值插入 xff0c 并且链表长度限制在10
  • SYD8821 IIC模块使用说明

    SYD8821是具有全球领先低功耗 RX 2 4mA 64 94 5dBm灵敏度 xff0c TX 4 3mA 64 0dBm输出功率 的蓝牙低功耗SOC芯片 xff0c 在极低电流下实现了优异的射频性能 xff0c 搭配176kB SRA
  • Python下载文件时出现乱码的解决方法之一:Content-Encoding: gzip

    之前写过一个简单的爬虫程序 xff0c 这次想试着再写一个下载固定文件的爬虫程序 写完之后发现下载的文件 xff0c 有些是可以正常打开的 xff0c 而有些是提示了编码错误 xff0c 用wireshark抓包 xff0c 过滤出http
  • Python爬虫判断url链接的是下载文件还是html文件

    最近在写一个网络爬虫的代码 xff0c 提供命令行来下载文件或者是打印根域名下指定节点及深度的子节点 用的是urllib2库 xff0c 算是比较简单 xff0c 但是功能并没有很强大 说重点吧 xff0c 在实际爬网页的过程中 xff0c
  • recvmsg和sendmsg函数

    在unp第14章讲了这两个函数 xff0c 但是只是讲了两个数据结构及参数而已 xff0c 所以自己想根据介绍来重构udp回射的客户端程序 但是sendmsg和recvmsg都遇到了问题 xff0c 并且纠结了很久 xff0c 所以在此记录
  • STL中map的[]运算导致程序挂掉的问题

    在项目的开发中 xff0c 使用 设置map变量时 xff0c 出现了Segment Fault的问题 xff0c 使用GDB bt命令得到调用栈 xff08 中间部分被我去掉了 xff09 如下 xff1a 0 0x00000000008

随机推荐

  • 结构体的vector resize()与初始化

    转自 xff1a https www cnblogs com kongse qi p 6798873 html 序 xff1a 我们在使用vector的时候可以自定义里面的数据类型 例如这样 xff1a struct Edge int fr
  • new(p) T1(value) 的操作

    最近开始看 STL源码剖析 xff0c 看到空间配置器的时候 xff0c 发现这么一段代码 xff1a template lt class T1 class T2 gt inline void construct T1 p const T2
  • Protobuf 编码及序列化的记录

    工作中用到了protobuf xff0c 然后之前在面试的时候面试官就问了一个问题 xff0c 如果将int32类型的字段的值设置为0 xff0c 那还会将该值进行序列化吗 xff1f 当时是懵了的 xff0c 因为自己还没有研究这部分 当
  • STM32驱动lcd1602,并口8位 6800时序

    STM32驱动lcd1602 xff0c 并口8位 6800时序 一 LCD1602 xff08 3 3V硬件版本 xff09 简介 1 1 引脚 引脚 xff0c lcd1602采用标准接口 xff0c 6800时序8位并行数据传输 第
  • CAN总线详解

    1 简介 CAN是控制器局域网络 Controller Area Network CAN 的简称 xff0c 是一种能够实现分布式实时控制的串行通信网络 优点 xff1a 传输速度最高到1Mbps xff0c 通信距离最远到10km xff
  • VS C#工程三【使用配置文件保存应用程序里的配置数据】[Resources找不到][打印log到VS窗口]【安全从工程删除图片】[发布exe并附带动态链接库dll]

    SYD8801是一款低功耗高性能蓝牙低功耗SOC xff0c 集成了高性能2 4GHz射频收发机 32位ARM Cortex M0处理器 128kB Flash存储器 以及丰富的数字接口 SYD8801片上集成了Balun无需阻抗匹配网络
  • 对Socket CAN的理解(2)——【Socket的原理及使用】

    转载请注明出处 xff1a http blog csdn net Righthek 谢谢 xff01 为了能够对Socket CAN的深入理解 xff0c 我们需要了解Socket的机制 Socket的中文翻译为 插座 xff0c 在计算机
  • 对Socket CAN的理解(3)——【Socket CAN发送数据流程】

    转载请注明出处 xff1a http blog csdn net Righthek 谢谢 xff01 对于本文 xff0c 我们将从用户层使用Socket CAN进行数据发送时 xff0c 数据从用户空间到底层驱动的整个通信流程 xff0c
  • 对Socket CAN的理解(4)——【Socket CAN接收数据流程】

    转载请注明出处 xff1a http blog csdn net Righthek 谢谢 xff01 现在我们来分析一下CAN总线的接收数据流程 xff0c 对于网络设备 xff0c 数据接收大体上采用中断 43 NAPI机制进行数据的接收
  • docker创建ubuntu16.04容器(上)

    容器三大基本概念 xff0c docker整个生命周期就是这三个概念 镜像 image Docker镜像就是一个只读的模板 镜像可以用来创建Docker容器 Docker提供了一个很简单的机制来创建镜像或者更新现有的镜像 xff0c 用户甚
  • SPDIF接口介绍

    一 S PDIF接口规范详解 S PDIF是sony和philips在80年代为一般家用器材所定制出来的一种数字讯号传输接口 xff0c 基本上是以AES EBU 也称AES3 专业用数字接口为参考然后做了一些小变动而成的家用版本 可以使用
  • 【音频】I2S协议详解

    一 I2S介绍 I2S Inter IC Sound 总线 又称 集成电路内置音频总线 xff0c 是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准 xff0c 该总线专门用于音频设备之间的数据传输 xff0c 广泛应用于各
  • linux 音频驱动实验

    文章目录 一 音频接口简介 1 为何需要音频编解码芯片2 WM8960简介3 I2S总线接口4 6u SAI简介 二 硬件原理图三 音频驱动使能 1 修改设备树 1 Wm8906 i2c接口设备树2 6u SAI接口设备树3 I MX6UL
  • 【UBI文件系统制作】-(1)UBI文件系统简介

    1 引言 在Linux 2 6 27以前 xff0c 谈到Flash文件系统 xff0c 大家很多时候多会想到CRAMFS JFFS2 YAFFS2等文件系统 它们也都是基于文件系统 43 MTD 43 Flash设备的架构 Linux 2
  • 分享篇 - Android 如何使用其他 APP 的 App ID 做微信分享

    1 需求背景 一些 APP 分享到微信 xff0c 朋友圈经常被封 如果在微信分享时 xff0c 我们将微信分享的 App ID 改成其他应用的 App ID xff0c 就可以使用其他 App 的名义进行分享 xff0c 而且分享出去的内
  • 缺少类型说明符 - 假定为 int。注意: C++ 不支持默认 int---->解决方法

    写了一个对应让其出的错误 xff08 其实也挺不容易的喔 xff09 错误如下 xff1a 1 gt d work win32project testeachotherclude testeachotherclude test2 h 9 e
  • 58同城Android端-最小插件化框架实战和原理分析

    目录 背景插件化需要了解的知识 2 1 类加载过程和类加载器 2 2 ClassLoader 的 findClass findLibrary findResource 2 3 DexClassLoader 的 oat 配置 2 4 Load
  • MetaX组件化框架

    目录 MetaX 框架介绍MetaX 是如何设计的MetaX 运行效果 1 MetaX 框架介绍 MetaX 是 58无线 Android 团队开发的一套增强版的组件化框架 xff0c 它制定了一系列的标准和规范 xff0c 来解决现有组件
  • 58同城首页腰部动态化技术选型(布局动态化)

    1 行业情况 1 1 基本概念介绍 1 1 1 Web混合 Web 前端和客户端的混合开发 使用 WebView 进行页面渲染 逻辑执行 xff1b 依赖客户端的能力需要通过 JSBridge 通信桥 的方式进行调用 xff0c 比如调用客
  • Java篇 - 四种引用(Reference)实战

    Java的垃圾回收 GC 是虚拟机自动管理的 xff0c 前面我有篇文章专门讲GC xff1a JVM篇 GC给你整明白 Java内存管理分为内存分配和内存回收 xff0c 都不需要程序员负责 xff0c 垃圾回收的机制主要是看对象是否有引