内存优化01

01背包问题解析 本文介绍了经典的01背包问题及其变种应用的解题方法。首先解释了01背包问题的基本概念和动态规划解法,给出了核心公式dp[i][j] = max(dp[i-1][j], v[i]+dp[i-1][j-w[i]])。然后通过多个洛谷例题展示了01背包问题的实际应用,包括采药问题、装箱问题等,并逐步介绍了从二维到一维的优化方法。最后提供了各题的C++代码实现,帮助读者理解如何将理论应用于实践。文章适合算法初学者学习01背包问题的基本解法及其优化策略。 阅读详情

前言

为什么内存优化?

在一个商业项目中,很有可能因为工程师的疏忽,导致代码质量不佳,影响到程序的运行效率,从而让用户感知到应用的卡顿、崩溃。而Android开发中,每个Android应用在手机上申请的内存空间都是有限的。虽然手机发展越来越快,可申请到的内存越来越大,但是也不能大手大脚,随便浪费应用可使用的内存空间。内存一旦不够时,你这个应用就会因为OOM(out of memory)而崩溃。因此,内存优化这一块内容,在开发应用时是非常重要的。

1 内存优化的关键点—避免内存泄露

内存优化中非常关键的一点,就是避免内存泄露。因为内存泄露会严重的导致内存浪费,所以避免内存泄露,是内存优化中必不可少的。

2 java中的四种引用类型

java引用类型不是指像int、char等这些基本的数据类型。java中的引用类型有四种:强引用、软引用、弱引用、虚引用。这四种引用类型,它们关于对象的可及性是由强到弱的。

2.1强引用

最常见的强引用方式如下:

        //强引用  对象类型 对象名 = new 对象构造方法();
        //比如下列代码
        String str = new String("abc");

在上述代码中,这个str对象就是强可及对象。强可及对象永远不会被GC回收。它宁愿被抛出OOM异常,也不会回收掉强可及对象。

清除强引用对象中的引用链如下:

        String str = new String("abc");
        //置空
        str = null;

2.2 软应用

软引用方式如下:

        //软引用SoftReference
        SoftReference<String> softReference = new SoftReference<String>(str);

在上述代码中,这个str对象就是软可及对象。当系统内存不足时,软可及对象会被GC回收。

清除软引用对象中的引用链可以通过模拟系统内存不足来清除,也可以手动清除,手动清除如下:

        SoftReference<String> softReference = new SoftReference<String>(str);
        softReference.clear();

2.3 弱引用

弱引用方式如下:

        //弱引用WeakReference
        WeakReference<String> weakReference = new WeakReference<>(str);

在上述代码中,这个str对象就是弱可及对象。当每次GC时,弱可及对象就会被回收。

清除弱引用对象中的引用链可以通过手动调用gc代码来清除,如下:

        WeakReference<String> weakReference = new WeakReference<>(str);
        System.gc();

当然,也可以通过类似软引用,调用clear()方法也可以。

2.4 虚引用

虚引用方式如下:

        //虚引用PhantomReference
        PhantomReference phantomReference = new PhantomReference<>(arg0, arg1);

虚引用一般在代码中出现的频率极低,主要目的是为了检测对象是否已经被系统回收。它在一些用来检测内存是否泄漏的开源项目中使用到过,如LeakCanary。

2.5 补充

  • 一个对象的可及性由最强的那个来决定。

  • System.gc()方法只会回收堆内存中存放的对象。

        String str = "abc";
        //弱引用WeakReference
        WeakReference<String> weakReference = new WeakReference<>(str);
        System.gc();

​ 像这样的代码,即使gc后,str对象仍然可以通过弱引用拿到。因为像”abc”这种,并没有存放在堆内 存中,它被存放在常量池里,所以gc不会去回收。

3 内存泄露的原因

对无用对象的引用一直未被释放,就会导致内存泄露。如果对象已经用不到了,但是因为疏忽,导致代码中对该无用对象的引用一直没有被清除掉,就会造成内存泄露。

比如你按back键关掉了一个Activity,那么这个Activity页面就暂时没用了。但是某个后台任务如果一直持有着对该Activity对象的引用,这个时候就会导致内存泄露。

4 检测内存泄露—LeakCanary

在全球最大的同性交友网站github中,有一个非常流行的开源项目LeakCanary,它能很方便的检测到当前开发的java项目中是否存在内存泄露。

5 LeakCanary的使用

5.1 官方使用文档描述

从LeakCanary的文档描述中,可以得知使用方式,简单翻译为如下步骤:

  1. 在你的项目中,找到moudle级别的build.gradle文件,并在dependencies标签里加上以下代码:

    dependencies {
    //... 你项目中以前声明的一些依赖
      debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
      releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
      testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
    }
  2. 在你Android项目中,找到先前写的Application类(PS:如果没有,那么请自行新建并在AndroidManifest中声明),并添加如下代码:

    public class ExampleApplication extends Application {
    
     @Override public void onCreate() {
       super.onCreate();
       if (LeakCanary.isInAnalyzerProcess(this)) {
         // This process is dedicated to LeakCanary for heap analysis.
         // You should not init your app in this process.
         return;
       }
       LeakCanary.install(this);
       // Normal app init code...
     }
    }
  3. 导入完毕!当你的应用出现内存泄露时,LeakCanary会在通知栏上进行通知,注意查看。下图是一个LeakCanary检测到内存泄露时的实示例。
    LeakCanary检测到内存泄露

5.2 检测Fragment

上述步骤默认会检测Activity,但是不会去检测Fragment,如果需要对某个Fragment检测的话,需要利用到LeakCanary的其他写法。

首先,在先前的Application类中,改写为以下代码:

public class MyApplication extends Application {

    public static RefWatcher mRefWatcher;

    @Override public void onCreate() {
        super.onCreate();
        //...
        mRefWatcher = LeakCanary.install(this);
        // Normal app init code...
    }
}   

然后在Fragment中的onDestroy方法中,去使用这个静态的RefWatcher进行观察,如果onDestroy了当前这个Fragment还没被回收,说明该Fragment产生了内存泄露。

@Override
public void onDestroy() {
    super.onDestroy();
    MyApplication.mRefWatcher.watch(this);
}

5.3 检测某个特定对象

有时候如果需要检测某个特定的可疑对象在某个时机下是否内存泄露,那么只需要执行如下代码

(假如对象名为someObjNeedGced):

    //...
    RefWatcher refWatcher = MyApplication.refWatcher;
    refWatcher.watch(someObjNeedGced);
    //...

当执行了refWatcher.watch方法时,如果这个对象还在内存中被其他对象引用,就会在 logcat 里看到内存泄漏的提示。

6 LeakCanary的原理简介

LeakCanary的代码执行流程图如下: leakCanary

LeakCanary_flow.jpg

LeakCanary 的机制如下:

  1. RefWatcher.watch() 会以监控对象来创建一个KeyedWeakReference 弱引用对象
  2. 在AndroidWatchExecutor的后台线程里,来检查弱引用已经被清除了,如果没被清除,则执行一次 GC
  3. 如果弱引用对象仍然没有被清除,说明内存泄漏了,系统就导出 hprof 文件,保存在 app 的文件系统目录下
  4. HeapAnalyzerService启动一个单独的进程,使用HeapAnalyzer来分析 hprof 文件。它使用另外一个开源库 HAHA。
  5. HeapAnalyzer 通过查找KeyedWeakReference 弱引用对象来查找内在泄漏
  6. HeapAnalyzer计算KeyedWeakReference所引用对象的最短强引用路径,来分析内存泄漏,并且构建出对象引用链出来。
  7. 内存泄漏信息送回给DisplayLeakService,它是运行在 app 进程里的一个服务。然后在设备通知栏显示内存泄漏信息。

7 常见的内存泄露

7.1 Actiivty内存泄露

7.1.1 内部类导致泄露

内部类实例会隐式的持有外部类的引用。

比如说在Activity中去创建一个内部类实例,然后在内部类实例中去执行一些需要耗时间的任务。任务在执行过程中,将Activity关掉,这个时候Activity对象是不会被释放的,因为那个内部类还持有着对Activity的引用。但是Activity此时已经是个没用的Activity了,所有这时,内存泄露就出现了。

隐式持有外部类的说明:内部类可以直接去调用外部类的方法,如果没有持有外部类的引用,内部类是没办法去调用外部类的属性和方法的,但是内部类又没有明显的去指定和声明引用,所以称之为隐式引用。

7.1.1.1 Thread线程

在Activity中创建一个内部类去继承Thread,然后让该Thread执行一些后台任务,未执行完时,关闭Activity,此时会内存泄露。核心代码如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startThread();
            }
        });
    }

    private void startThread() {
        Thread thread = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    SystemClock.sleep(1000);
                }
            }
        };
        thread.start();
    }

}

当点击页面按钮执行startThread()后,再按下back键关闭Activity,几秒后LeakCanary就会提示内存泄露了。

为了避免此种Thread相关内存泄露,只需要避免这个内部类去隐式引用外部类Activity即可。

解决方案:让这个内部类声明为静态类。代码如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...与先前相比未做变化,不再描述
    }

    private void startThread() {
        Thread thread = new MyStaticThread();
        thread.start();
    }

    private static class MyStaticThread extends Thread {

        @Override
        public void run() {
            for (int i = 0; i < 200; i++) {
                SystemClock.sleep(1000);
            }
        }
    }
}

这样声明为静态类后,该内部类将不会再去隐式持有外部类的应用。

如果像这样的循环操作,为了效率和优化,建议通过申明一个boolean类型的标志位来控制后台任务。比如在外部类Activity的onDestory退出方法中,将boolean值进行修改,使后台任务退出循环。代码如下:

public class MainActivity extends AppCompatActivity {

    ...
    //Activity页面是否已经destroy
    private static boolean isDestroy = false;

    private static class MyStaticThread extends Thread {

        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                if(!isDestroy){
                    SystemClock.sleep(1000);
                }
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        isDestroy = true;
    }
}

因为申明为了静态内部类,该内部类不再持有外部类Activity的引用,所以此时不能再去使用外部类中的方法、变量。除非外部类的那些方法、变量是静态的。

Q:在防止内存泄露的前提下,如果一定要去使用那些外部类中非静态的方法、变量,该怎么做?

A:通过使用弱引用或者软引用的方式,来引用外部类Activity。代码如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
    }

    private void startThread() {
        Thread thread = new MyStaticThread(MainActivity.this);
        thread.start();
    }

    private  boolean isDestroy = false;//Activity页面是否已经destroy

    private static class MyStaticThread extends Thread {

        private WeakReference<MainActivity> softReference = null;

        MyStaticThread(MainActivity mainActivity){
            this.softReference = new WeakReference<MainActivity>(mainActivity);
        }

        @Override
        public void run() {
            //能够isDestroy变量是非静态的,它属于MainActivity,我们只要拿到了MainActivity对象,就能拿到isDestroy
            MainActivity mainActivity = softReference.get();
            for (int i = 0; i < 200; i++) {
                //使用前最好对MainActivity对象做非空判断,如果它已经被回收,就不再执行后台任务
                if(mainActivity!=null&&!mainActivity.isDestroy){
                    SystemClock.sleep(1000);
                }
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        isDestroy = true;
    }
}
7.1.1.2 Handler

在使用Handler时,经常可以看到有人在Activity、Fragment中写过内部类形式的Handler,比如说写一个内部类形式的handler来执行一个延时的任务,像这样:

public class MainActivity extends AppCompatActivity {

    private static final int MESSAGE_DELAY = 0;
    private Button mButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.button);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startDelayTask();
            }
        });
    }

    private void startDelayTask() {
        //发送一条消息,该消息会被延时10秒后才处理
        Message message = Message.obtain();
        message.obj = "按钮点击15秒后再弹出";
        message.what = MESSAGE_DELAY;
        mHandler.sendMessageDelayed(message, 15000);
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_DELAY:
                    Toast.makeText(MainActivity.this, (String) msg.obj, Toast.LENGTH_SHORT).show();
                    mButton.setText("延时修改了按钮的文本");
                    break;
            }
        }
    };
}

当点击了按钮后会发送出一条消息,该消息将会15秒后再进行处理,如果中途退出Activity,不一会LeakCanary就会检测到内存泄露。

上述代码发生内存泄露也是因为内部类持有外部类的引用。这个内部类Handler会拿着外部类Activity的引用,而那个Message又拿着Handler的引用。这个Message又要在消息队列里排队等着被handler中的死循环来取消息。从而形成了一个引用链,最后导致关于外部类Activity的引用不会被释放。

该情况的的解决方案,是与上一节的Thread线程相同的。只要将Handler设置为static的静态内部类方式,就解决了handler持有外部类引用的问题。

如果handler已申明为静态内部类,那么Handler就不再持有外部类的引用,无法使用外部类中非静态的方法、变量了。

如果想在避免内存泄露的同时,想使用非静态的方法、变量,同样可以用弱(软)引用来做。

public class MainActivity extends AppCompatActivity {

    private static final int MESSAGE_DELAY = 0;
    private Button mButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
    }

    private void startDelayTask() {
        //发送一条消息,该消息会被延时10秒后才处理
        ...
    }

    private Handler mHandler = new InsideHandler(MainActivity.this);

    private static class InsideHandler extends Handler {
        private WeakReference<MainActivity> mSoftReference;

        InsideHandler(MainActivity activity) {
            mSoftReference = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity mainActivity = mSoftReference.get();
            if (mainActivity != null) {
                switch (msg.what) {
                    case MESSAGE_DELAY:
                        Toast.makeText(mainActivity, (String) msg.obj, Toast.LENGTH_SHORT).show();
                        //通过软引用中的mainActivity可以拿到那个非静态的button对象
                        mainActivity.mButton.setText("延时修改了按钮的文本");
                        break;
                }
            }
        }
    }
}

最后,更完美的做法是在这些做法的基础上,再添加这段逻辑:当Activity页面退出时,将handler中的所有消息进行移除,做到滴水不漏。

其实就是在onDestroy中写上:

@Override
protected void onDestroy() {
    super.onDestroy();
    //参数为null时,handler中所有消息和回调都会被移除
    mHandler.removeCallbacksAndMessages(null);
}

PS:弱引用和软引用的区别:弱引用会很容易被回收掉,软引用没那么快。如果你希望能尽快清掉这块内存使用就使用弱引用;如果想在内存实在不足的情况下才清掉,使用软引用。

下图是在内部类Handler使用软引用时LeakCanary出现的提示。

20161014152803

因为使用软引用,GC会有点偷懒,所以leakCanary会检测到一些异常,出现这样的提示。

7.1.1.3 非静态内部类的静态实例

有时候会使用,代码如下:

public class MainActivity extends AppCompatActivity {

    private static User sUser = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
    }

    private void initData() {
        if(sUser==null){
            sUser = new User();
        }
    }

    private class User{
        User(){
        }
    }
}

在代码中,非静态的内部类创建了一个静态实例。非静态内部类会持有外部类Activity的引用,后来又创建了一个这个内部类的静态实例。

这个静态实例不会在Activity被关掉时一块被回收(静态实例的生命周期跟Activity可不一样,你Activity挂了,但是写在Activity中的静态实例还是会在,静态实例的生命周期跟应用的生命周期一样长)。

非静态内部类持有外部引用,而该内部类的静态实例不会及时回收,所以才导致了内存泄露。

解决方案:将内部类申明为静态的内部类。

public class MainActivity extends AppCompatActivity {

    ...

    private static class User{
        ...
    }
}
7.1.2 Context导致泄露

有时候我们会通过静态的方式去引用一个Activity。比如:创建一个以单例的形式存在的类,并且这个类需要做关于UI的处理,所以传递了一个Context进来,代码如下:

public class ToastManager {
    private Context mContext;
    ToastManager(Context context){
        mContext = context;
    }

    private static ToastManager mManager = null;

    public void showToast(String str){
        if(mContext==null){
            return;
        }
        Toast.makeText(mContext, str, Toast.LENGTH_SHORT).show();
    }

    public static ToastManager getInstance(Context context){
        if(mManager==null){
            synchronized (ToastManager.class){
                if(mManager==null){
                    mManager = new ToastManager(context);
                }
            }
        }
        return mManager;
    }
}

而在使用时是这样写的:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        ToastManager instance = ToastManager.getInstance(MainActivity.this);
    }
}

这个时候代码也会发生内存泄露。因为静态实例比Activity生命周期长,你在使用静态类时将Activity作为context参数传了进来,即时Activity被关掉,但是静态实例中还保有对它的应用,所以会导致Activity没法被及时回收,造成内存泄露。

解决方案:在传Context上下文参数时,尽量传跟Application应用相同生命周期的Context。比如getApplicationContext(),因为静态实例的生命周期跟应用Application一致。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ToastManager instance = ToastManager.getInstance(getApplicationContext());
    }
}
7.1.2.1 补充:Context的作用域

系统中的Context的具体实现子类有:Activity、Application、Service。

虽然Context能做很多事,但并不是随便拿到一个Context实例就可以为所欲为,它的使用还是有一些规则限制的。在绝大多数场景下,Activity、Service和Application这三种类型的Context都是可以通用的。不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。

出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。

Context

上图中Application和Service所不推荐的两种使用情况:

  1. 如果我们用ApplicationContext去启动一个LaunchMode为standard的Activity的时候会报错

    javaandroid.util.AndroidRuntimeException: Calling startActivity from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
    

    这是因为非Activity类型的Context并没有所谓的任务栈,所以待启动的Activity就找不到栈了。解决这个问题的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就为它创建一个新的任务栈,而此时Activity是以singleTask模式启动的。所有这种用Application启动Activity的方式不推荐使用,Service的原因跟Application一致。

  2. 在Application和Service中去layout inflate也是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。所以这种方式也不推荐使用。一句话总结:凡是跟UI相关的,都建议使用Activity做为Context来处理;其他的一些操作,Service,Activity,Application等实例Context都可以,当然了,注意Context引用的持有,防止内存泄漏。

7.2 监听器的注销

在Android程序里面存在很多需要register与unregister的监听器,我们需要确保在合适的时候及时unregister那些监听器。自己手动add的listener,需要记得及时remove这个listener。比如说:广播接收者BroadcastReceiver、EventBus等。

7.3 Cursor对象和IO流是否及时关闭

在程序中我们经常会进行查询数据库的操作,但时常会存在不小心使用Cursor之后没有及时关闭的情况。这些Cursor的泄露,反复多次出现的话会对内存管理产生很大的负面影响,我们需要谨记对Cursor对象的及时关闭。IO流同理。

7.4 WebView的泄漏

Android中的WebView存在很大的兼容性问题,不仅仅是Android系统版本的不同对WebView产生很大的差异,另外在不同手机厂商出货的ROM里面,WebView也存在着很大的差异。所以通常根治这个问题的办法是为展示WebView的页面开启另外一个进程,通过AIDL与主进程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放。

LDO稳定性分析 根据补偿方式和拓扑结构的不同,三个极点之间的相互位置有可能发生变化,但 是 LDO 通常采用较大的输出电容 Cout,以及较大的调整管也会导致较大的 Cpar, 所以 p1、p2 通常位于较低的频率处,而 p3 和 z1 则由于 Resr较小会位于较高的频 率处。不过幸运的是,对于较大的 Cout来说,只要选择合适的 Resr,就可以将左半平面零点 Z1 的频率降低,甚至使 Z1 进入单位增益带宽内,左半平面零点的进入将对相位有所改善,这不仅可以消除振荡的风险,还可以在单位增益频率处得到较好的相位裕度。 阅读详情

相关推荐

不写代码,Cursor 10分钟开发1个APP!保姆级教程喂饭来了

做出第一个能运行的app,那种成就感真的很棒。虽然过程可能有点折腾,但只要耐心一点,问题都能解决。记住,遇到问题不要慌,现在有AI帮忙,大部分问题都能快速解决。重要的是迈出第一步,开始动手实践。你的第一个app可能很简单,但这是一个很好的开始。随着经验积累,你能做出的东西会越来越酷。现在就开始行动吧,说不定下个月你就能向朋友炫耀你自己做的app了!

chenhong007的博客 1551

Android:最全面&详细的性能优化攻略(含内存优化、内存泄漏、绘制优化、布局优化、图片优化、APK优化、多线程优化、列表优化等)

前言:佛教中有一句话:初学者的心态,拥有初学者心态是件了不起的事情。真正的大师永远怀有一颗学徒的心。 一、概述 在Android中,性能优化非常重要,因为这决定这一个APP的质量好坏,一个更快更稳更省的app能给用户更优秀的体验。 更快:App流畅不卡顿,快速响应; 更稳:App稳定运行,程序不崩溃(Crash)和无响应(ANR); 更省:节省资...

https://juejin.cn/user/1654096907477549/posts 1万+

UltraScale+ FPGA中GTY收发器参考时钟的跨Bank共享机制解析

本文深入解析了UltraScale+ FPGA中GTY收发器参考时钟的跨Bank共享机制,详细介绍了时钟布线架构、SLR区域限制及Vivado工程配置要点。通过实际案例和优化建议,帮助工程师解决高速串行通信中的时钟共享问题,提升系统性能。

weixin_29280219的博客 632

Android 内存优化

一、重识内存优化 Android给每个应用进程分配的内存都是非常有限的,那么,为什么不能把图片下载下来都放到磁盘中呢?那是因为放在 内存 中,展示会更 “快”,快的原因有两点,如下所示: 1)、硬件快:内存本身读取、存入速度快。 2)、复用快:解码成果有效保存,复用时,直接使用解码后对象,而不是再做一次图像解码。 这里说一下解码的概念。Android系统要在屏幕上展示图片的时候只认 “像素缓冲”,而这也是大多数操作系统的特征。而我们 常见的jpg,png等图片格式,都是把 “像素缓冲” 使用不同的手段

十年代码写十年 2744

进阶数据结构——链式前向星

链式前向星是一种进阶的数据结构,主要用于图的存储。我希望大家先看这个视频(蓝色字体点进去) 不然你很难理解的。在图论中,图的存储方式主要有邻接矩阵和邻接表两种。邻接矩阵虽然建图简单方便,但占内存较大,节点数量稍微大点的题目就难以使用(适合稠密图);而邻接表虽然节省空间,但其指针形式难写易错,且vector形式的读取速度较慢,易导致大数据下的超时(TLE)。针对上述问题,链式前向星作为一种折中的方式应运而生。链式前向星本质上是一种静态链表,它将数据以链表形式连接在一起。

ycs66的博客 2137

深入探索 Android 内存优化(炼狱级别)

前言 成为一名优秀的Android开发,需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样~。 本篇是 Android 内存优化的进阶篇,难度可以说达到了炼狱级别,建议对内存优化不是非常熟悉的仔细看看前篇文章:Android性能优化之内存优化,其中详细分析了以下几大模块: 1)、Android的内存管理机制 2)、优化内存的意义 3)、避免内存泄漏 4)、优化内存空间 5)、图片管理模块的设计与实现 如果你对以上基础内容都比较了解了,那么我们便开始 Android 内...

idaretobe的专栏 4148

pix4d安装步骤

pix4d三维模型软件的安装步骤

Little_Bee_27000的博客 1万+

Android内存优化的10条建议

合理设置应用的minSdkVersion和targetSdkVersion,使应用可以运行在更多设备上,这可以提高内存利用效率。 避免在Application和Activity的onCreate方法中做过多工作,这会占用过多内存。可以将不必要的初始化工作放到onStart或延迟加载。 避免在UI线程中进行过多工作,可以使用AsyncTask或者Thread执行耗时操作,避免ANR问题发生。 重用已有对象,避免重复创建对象,这会产生大量垃圾对象,增加GC负担。可以使用对象池技术重用对象。 优化布局,减少视图

wayne214的博客 1061

14、网络切片技术:原理、应用与挑战

本文详细探讨了网络切片技术的原理、应用场景及其在5G网络中的重要性。网络切片作为一种关键的网络架构技术,能够在同一物理网络上创建多个独立的逻辑网络,以满足不同应用对安全性、可靠性和性能的多样化需求。文章深入分析了网络切片在5G、SDN/NFV等技术背景下的发展与挑战,并探讨了其在制造业、交通运输、零售等多个行业的实际应用。此外,还介绍了网络切片的部署流程、未来发展趋势以及相关技术难点,为读者全面了解网络切片技术提供了有价值的参考。

palm99的专栏 254

微信小程序数据采集实战:抓包+接口分析+动态数据全流程

微信小程序爬虫虽然比普通网页爬虫复杂,但只要掌握了正确的方法,其实并没有想象中那么难。核心思路就是:先绕过证书校验抓包,然后反编译小程序找到加密逻辑,最后用Python模拟相同的加密和解密过程。需要特别提醒的是,本文仅供技术研究使用。请遵守《网络安全法》和相关法律法规,不要爬取他人的敏感数据,不要将爬虫技术用于非法用途。尊重他人的知识产权和数据安全,才能让技术发挥真正的价值。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1093

Android性能优化系列:内存优化

实践App内存优化:如何有序地做内存分析与优化

Vincent的博客 7495

【SOUND】Linux音频子系统ASoC的驱动框架总分析

ALSA系统在芯片(ASoC)层的总体项目目标是为嵌入式芯片处理器(例如PXA2XX,AU1X00,IMX等)和便携式音频编解码器提供更好的ALSA支持。 在进入ASoC子系统之前,内核对SoC音频有一些支持,但是它有一些局限性:ASoC层旨在解决这些问题并提供以下内容特征 :为了实现这一切,ASoC基本上将嵌入式音频系统分为多个可重复使用的组件驱动程序:另外,这里提下OSS(Open Sound System)这个基本被弃用的音频框架。 可以参考Linux OSS架构声卡驱动框架 2.3 ASoC重要

wang 恒 的博客 1781

01 Java内存优化之jvm三代关系

1 jvm的新生代、老年代、永久代关系 (1)新生代 新生代分为三个区域,一个Eden区和两个Survivor区,它们之间的比例为(8:1:1)。通常情况下,对象主要分配在新生代的Eden区上,少数情况下也可能会直接分配在老年代中。Java虚拟机每次使用新生代中的Eden和其中一块Survivor(From),在经过一次Minor GC后,将Eden和Survivor中还存活的对象一次性地复制到另一块Survivor空间上(这里使用的复制算法进行GC),最后清理掉Eden和刚才用过的Survivor(F

weixin_44342753的博客 424

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

分享之系列篇01- Android - 内存优化

HashMap中默认的存储大小是一个容量为16的数组,所以当我们创建出一个HashMap对象时,即使里面没有任何元素,也要分别一块内存空间给它,而且当我们不断的向HashMap里put数据时,达到一定的容量限制时HashMap的空间将会扩大,从图示中可以看出当HashMap扩容容量过多,元素较少时会产生内存使用不平衡,即浪费不少内存. HashMap会涉及一个要求:key与value必须为对象类型,而不能为基本类型, 这就导致了本可以使用基本类型的数据必须转换为其对象包装类型,也会浪费一些内存。

zhang1317725的专栏 418

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

性能优化01_内存泄漏

Android性能优化汇总 一 内存泄漏 1.什么是内存泄露: 内存不在GC掌控之内了 当一个对象已经不需要再使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用从而就导致对象不能被回收。这种导致了本该被回收的对象不能被回收而停留在堆内存中,就产生了内存泄漏。 C/C++ 自己去分配内存和释放内存–手动管理 malloc free 了解java的GC内存回收机制:某对象不再有任何的引...

baopengjian的专栏 527

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Redis内存管理和优化01

注.这是在学完课程之后写的文章,并没有经过实战的检验,希望大家不要不信也不要全信,尽信书不如无书。 redis版本说明:redis3.x 1.底层设计 图 1-1 redis给使用者提供了5种数据类型,其实在底层实现时只使用了一个结构体来实现,如图 1-1所示 .可以看出reids里面有不超过16种的类型和不超过16种的编码类型,一种类型有多种编码方式。refcount一看就知道是...

pengcheng2013的博客 284

POJ 3624 Charm Bracelet (01背包 + 内存优化)

#include #define MAX_CHARMS 3402 #define MAX_WEIGHT 12880 typedef struct Charm{ int weight; int desirability; }Charm; Charm CharmArray[MAX_CHARMS + 1]; int numOfCharms; int weightLimit; int maxDes

TiuTiu Zone 522

Android代码内存优化建议-OnTrimMemory优化01

OnTrimMemory 回调是 Android 4.0 之后提供的一个API,这个 API 是提供给开发者的,它的主要作用是提示开发者在系统内存不足的时候,通过处理部分资源来释放内存,从而避免被 Android 系统杀死。这样应用在下一次启动的时候,速度就会比较快。 本文通过问答的方式,从各个方面来讲解 OnTrimMemory 回调的使用过程和效果。想要开发高性能且用户体验良好的 And

流星火的博客 768

Android-性能优化01-布局优化&绘制优化&内存泄露优化

Android性能优化之--布局的优化和绘制优化: Andoid性能优化包括:                       1,布局优化,                       2,绘制优化,                       3,内存泄露优化,                       4,响应速度的优化,                       5,l

一条咸鱼 1025

牛客 Bessie's Weight Problem (01背包,空间优化)

题目链接 题意 一头牛最多能吃H kg的草,这里有N堆草,每堆草一旦选择就必须吃完,求牛最多能吃多少kg的草 思路 很明显,这是一个01背包问题,然而最朴素的01背包会内存超限,所以要进行空间优化,这里采用背包九讲里提到的空间优化方式,很轻松就解决了。 代码 #include <iostream> #include <algorithm> #include <stri...

reedthink的博客 466

01背包与完全背包微妙的区别

01背包跟完全背包微妙的区别: //完全背包 void solve1() { memset(d, 0, sizeof(d)); for(int i = 0; i < N; i++) for(int j = 0; j <= W; j++) if(j < w[i]) d[i+1][j] = d[i][j]; else d[i+1][j] = max(d[

NightWind 1547

学习笔记(01):Redis内存管理和优化-Redis内存管理和优化(下)

Redis是一个高性能的开源NOSQL内存数据库。本次分享主要介绍Redis内存的管理机制,Redis内存使用情况分析定位,阿里云Redis相关优化,以及相关的佳实践。...

claireliu(Java) 343

Java 性能 01 - JVM 堆内存参数优化(-Xms/-Xmx)

Java JVM堆内存参数优化指南 本文深入探讨JVM堆内存参数-Xms和-Xmx的优化策略。作为Java性能调优的核心参数,它们控制堆内存的初始大小和最大容量。文章首先解析堆内存结构,包括年轻代、老年代和元空间,然后通过实验对比不同参数配置下的GC表现。测试显示:小堆(-Xms256m -Xmx512m)导致频繁GC和性能下降;中等堆(-Xms512m -Xmx512m)性能提升明显;大堆(-Xms1g -Xmx1g)则GC最少。文章建议将初始堆和最大堆设为相同值以避免扩容开销,并推荐结合应用场景和监控工

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

HDU2602(01背包模板题)

01背包题集 Bone Collector----HDU2602 #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int maxn = 1001; struct info{ int p; ...

weixin_43808717的博客 284

C++:01.从C到C++:浅拷贝,优化规则,内存分配

new int[20]和new int[20]() 有()初始化为该类型的默认值       没有(为随机值)     开辟单个元素可以在()内随意初始化,但开辟数组是()内不能写入数字 类的成员方法在类体内定义和在类外定义: 1.类外定义的成员方法,在方法名字前面加类类型的作用域 2.类外定义的在调用时,有函数的正常开销(函数运行需要再栈上开辟内存);在类体内定义的函数,都被自动处理成inl...

V的博客 469
上一篇: AES加密
下一篇: 内存优化02
CDCK
博客等级 码龄9年 0粉丝 2原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值