Android 内存泄漏常见情况1 静态泄漏

1,内存泄漏到本质是该释放的对象被持久化的对象引用了,造成持久化的常见情况有1,静态持久化 2,线程持久化

线程持久化

因为存活的线程是有dvk虚拟久直接持有,所以存活的线程都是持久化的

内存泄漏1:静态Activities(static Activities)

代码如下:
MainActivity.Java

public class MainActivity extends AppCompatActivity {
    private static MainActivity activity;
    TextView saButton;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        saButton = (TextView) findViewById(R.id.text);
        saButton.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                setStaticActivity();
                nextActivity();
            }
        });
    }
    void setStaticActivity() {
        activity = this;
    }

    void nextActivity(){
        startActivity(new Intent(this,RegisterActivity.class));
        SystemClock.sleep(1000);
        finish();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //使用LeakCanary观察是否有内存泄漏
        MyApplication.getRefWatcher().watch(this);
    }
}

 

LeakCanary检测出的内存泄漏:

这里写图片描述

为什么?
在上面代码中,我们声明了一个静态的Activity变量并且在TextView的OnClick事件里引用了当前正在运行的Activity实例,所以如果在activity的生命周期结束之前没有清除这个引用,则会引起内存泄漏。因为声明的activity是静态的,会常驻内存,如果该对象不清除,则垃圾回收器无法回收变量。

怎么解决?
*简单的方法是在onDestory方法中将静态变量activity置空,这样垃圾回收器就可以将静态变量回收。

@Override
    protected void onDestroy() {
        super.onDestroy();
        activity = null;
        //使用LeakCanary观察是否有内存泄漏
        MyApplication.getRefWatcher().watch(this);
    }

不使用静态activity,或给静态activity赋值时,考虑赋值的activity生命周期是不是全局的,或者在静态activity使用完后及时释放

内存泄漏2:静态View

代码如下:
MainActivity.java

    ...
    private static View view;
    TextView saButton;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        saButton = (TextView) findViewById(R.id.text);
        saButton.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                setStaticView();
                nextActivity();
            }
        });
    }
    void setStaticView() {
        view = findViewById(R.id.sv_view);
    }
    ...

 

LeakCanary检测到的内存泄漏

这里写图片描述

为什么?
上面代码看似没有问题,在Activity里声明一个静态变量view,然后初始化,当Activity生命周期结束了内存也释放了,但是LeakCanary却显示出现了内存泄漏,为什么?问题出在这里,View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity,所以当activity生命周期结束了,静态View没有清除掉,还持有activity的引用,因此内存泄漏了。

怎么解决?
在onDestroy方法里将静态变量置空。

@Override
protected void onDestroy() {
    super.onDestroy();
    view = null;
    MyApplication.getRefWatcher().watch(this);
} 
不使用静态view,或在activity关闭时将静态view赋值为null

内存泄漏3:静态内部类

代码如下:
MainActivity.java

private static Object inner;
void createInnerClass() {
    class InnerClass {
    }
    inner = new InnerClass();
}

View icButton = findViewById(R.id.ic_button);
icButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        createInnerClass();
        nextActivity();
    }
});
 

使用LeakCanary检测到的内存泄漏:

这里写图片描述

为什么?
非静态内部类会持有外部类的引用,在上面代码中内部类持有Activity的引用,因此inner会一直持有Activity,如果Activity生命周期结束没有清除这个引用,这样就发生了内存泄漏。

怎么解决?
因为非静态内部类隐式持有外部类的强引用,所以我们将内部类声明成静态的就可以了。

void createInnerClass() {
    static class InnerClass {
    }
    inner = new InnerClass();
}
 

内存泄漏3:静态Drawable

当一个Drawable附加到一个 View上时,
View会将其作为一个callback设定到Drawable上。意味着Drawable拥有一个View的引用,上面说了view会有上下文的引用

 

 

内存泄漏4:静态集合中对象没清理造成的内存泄漏

我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。

 

内存泄漏5:单例导致内存泄露

单例模式在Android开发中会经常用到,但是如果使用不当就会导致内存泄露。因为单例的静态特性使得它的生命周期同应用的生命周期一样长,如果一个对象已经没有用处了,但是单例还持有它的引用,那么在整个应用程序的生命周期它都不能正常被回收,从而导致内存泄露。

public class AppSettings {

    private static AppSettings sInstance;
    private Context mContext;

    private AppSettings(Context context) {
        this.mContext = context;
    }

    public static AppSettings getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new AppSettings(context);
        }
        return sInstance;
    }
}

像上面代码中这样的单例,如果我们在调用getInstance(Context context)方法的时候传入的context参数是ActivityService等上下文,就会导致内存泄露。

Activity为例,当我们启动一个Activity,并调用getInstance(Context context)方法去获取AppSettings的单例,传入Activity.this作为context,这样AppSettings类的单例sInstance就持有了Activity的引用,当我们退出Activity时,该Activity就没有用了,但是因为sIntance作为静态单例(在应用程序的整个生命周期中存在)会继续持有这个Activity的引用,导致这个Activity对象无法被回收释放,这就造成了内存泄露。

为了避免这样单例导致内存泄露,我们可以将context参数改为全局的上下文:

private AppSettings(Context context) {
    this.mContext = context.getApplicationContext();
}

全局的上下文Application Context就是应用程序的上下文,和单例的生命周期一样长,这样就避免了内存泄漏。

单例模式对应应用程序的生命周期,所以我们在构造单例的时候尽量避免使用Activity的上下文,而是使用Application的上下文。

Android开发常见的Activity中内存泄漏及解决办法

上一篇文章楼主提到由Context引发的内存泄漏,在这一篇文章里,我们来谈谈Android开发中常见的Activity内存泄漏及解决办法。本文将会以“为什么”“怎么解决”的方式来介绍这几种内存泄漏。
在开篇之前,先来了解一下什么是内存泄漏。

什么是内存泄漏?

内存泄漏是当程序不再使用到的内存时,释放内存失败而产生了无用的内存消耗。内存泄漏并不是指物理上的内存消失,这里的内存泄漏是值由程序分配的内存但是由于程序逻辑错误而导致程序失去了对该内存的控制,使得内存浪费。

怎样会导致内存泄漏?

  • 资源对象没关闭造成的内存泄漏,如查询数据库后没有关闭游标cursor
  • 构造Adapter时,没有使用 convertView 重用
  • Bitmap对象不在使用时调用recycle()释放内存
  • 对象被生命周期长的对象引用,如activity被静态集合引用导致activity不能释放

在接下来的篇幅里,我们重点讲有关Activity常见的内存泄漏。

内存泄漏1:静态Activities(static Activities)

代码如下:
MainActivity.Java

public class MainActivity extends AppCompatActivity {
    private static MainActivity activity;
    TextView saButton;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        saButton = (TextView) findViewById(R.id.text);
        saButton.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                setStaticActivity();
                nextActivity();
            }
        });
    }
    void setStaticActivity() {
        activity = this;
    }

    void nextActivity(){
        startActivity(new Intent(this,RegisterActivity.class));
        SystemClock.sleep(1000);
        finish();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //使用LeakCanary观察是否有内存泄漏
        MyApplication.getRefWatcher().watch(this);
    }
}

LeakCanary检测出的内存泄漏:

这里写图片描述

为什么?
在上面代码中,我们声明了一个静态的Activity变量并且在TextView的OnClick事件里引用了当前正在运行的Activity实例,所以如果在activity的生命周期结束之前没有清除这个引用,则会引起内存泄漏。因为声明的activity是静态的,会常驻内存,如果该对象不清除,则垃圾回收器无法回收变量。

怎么解决?
*简单的方法是在onDestory方法中将静态变量activity置空,这样垃圾回收器就可以将静态变量回收。

@Override
    protected void onDestroy() {
        super.onDestroy();
        activity = null;
        //使用LeakCanary观察是否有内存泄漏
        MyApplication.getRefWatcher().watch(this);
    }

内存泄漏2:静态View

代码如下:
MainActivity.java

    ...
    private static View view;
    TextView saButton;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        saButton = (TextView) findViewById(R.id.text);
        saButton.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                setStaticView();
                nextActivity();
            }
        });
    }
    void setStaticView() {
        view = findViewById(R.id.sv_view);
    }
    ...

LeakCanary检测到的内存泄漏

这里写图片描述

为什么?
上面代码看似没有问题,在Activity里声明一个静态变量view,然后初始化,当Activity生命周期结束了内存也释放了,但是LeakCanary却显示出现了内存泄漏,为什么?问题出在这里,View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity,所以当activity生命周期结束了,静态View没有清除掉,还持有activity的引用,因此内存泄漏了。

怎么解决?
在onDestroy方法里将静态变量置空。

@Override
protected void onDestroy() {
    super.onDestroy();
    view = null;
    MyApplication.getRefWatcher().watch(this);
} 

内存泄漏3:内部类

代码如下:
MainActivity.java

private static Object inner;
void createInnerClass() {
    class InnerClass {
    }
    inner = new InnerClass();
}

View icButton = findViewById(R.id.ic_button);
icButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        createInnerClass();
        nextActivity();
    }
});

使用LeakCanary检测到的内存泄漏:

这里写图片描述

为什么?
非静态内部类会持有外部类的引用,在上面代码中内部类持有Activity的引用,因此inner会一直持有Activity,如果Activity生命周期结束没有清除这个引用,这样就发生了内存泄漏。

怎么解决?
因为非静态内部类隐式持有外部类的强引用,所以我们将内部类声明成静态的就可以了。

void createInnerClass() {
    static class InnerClass {
    }
    inner = new InnerClass();
}

内存泄漏4:匿名类

void startAsyncTask() {
    new AsyncTask<Void, Void, Void>() {
        @Override protected Void doInBackground(Void... params) {
            while(true);
        }
    }.execute();
}

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View aicButton = findViewById(R.id.at_button);
aicButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        startAsyncTask();
        nextActivity();
    }
});

使用LeakCanary检测到的内存泄漏:

这里写图片描述

为什么?
上面代码在activity中创建了一个匿名类AsyncTask,匿名类和非静态内部类相同,会持有外部类对象,这里也就是activity,因此如果你在Activity里声明且实例化一个匿名的AsyncTask对象,则可能会发生内存泄漏,如果这个线程在Activity销毁后还一直在后台执行,那这个线程会继续持有这个Activity的引用从而不会被GC回收,直到线程执行完成。

怎么解决?
自定义静态AsyncTask类,并且让AsyncTask的周期和Activity周期保持一致,也就是在Activity生命周期结束时要将AsyncTask cancel掉。

内存泄漏5:Handler

代码如下:
MainActivity.java

...
void createHandler() {
    new Handler() {
        @Override public void handleMessage(Message message) {
            super.handleMessage(message);
        }
    }.postDelayed(new Runnable() {
        @Override public void run() {
            while(true);
        }
    }, 1000);
}

...
View hButton = findViewById(R.id.h_button);
hButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        createHandler();
        nextActivity();
    }
});
...

为什么?
当Android Application启动以后,framework会首先帮助我们完成UI线程的消息循环,也就是在UI线程中,Loop、MessageQueue、Message等等这些实例已经由framework帮我们实现了。所有的Application主要事件,比如Activity的生命周期方法、Button的点击事件都包含在这个Message里面,这些Message都会加入到MessageQueue中去,所以,UI线程的消息循环贯穿于整个Application生命周期,所以当你在UI线程中生成Handler的实例,就会持有Loop以及MessageQueue的引用。并且在Java中非静态内部类和匿名内持有外部类的引用,而静态内部类则不会持有外部类的引用。

怎么解决?
可以由上面的结论看出,产生泄漏的根源在于匿名类持有Activity的引用,因此可以自定义Handler和Runnable类并声明成静态的内部类,来解除和Activity的引用。

内存泄漏6:Thread

代码如下:
MainActivity.java

void spawnThread() {
    new Thread() {
        @Override public void run() {
            while(true);
        }
    }.start();
}

View tButton = findViewById(R.id.t_button);
tButton.setOnClickListener(new View.OnClickListener() {
  @Override public void onClick(View v) {
      spawnThread();
      nextActivity();
  }
});

为什么?
同AsyncTask一样,这里就不过多赘述。

怎么解决?
那我们自定义Thread并声明成static这样可以吗?其实这样的做法并不推荐,因为Thread位于GC根部,DVM会和所有的活动线程保持hard references关系,所以运行中的Thread*不会被GC无端回收了,所以正确的解决办法是在自定义静态内部类的基础上给线程加上取消机制,因此我们可以在Activity的onDestroy方法中将thread关闭掉。

内存泄漏7:Timer Tasks

代码如下:
MainActivity.java

void scheduleTimer() {
    new Timer().schedule(new TimerTask() {
        @Override
        public void run() {
            while(true);
        }
    },1000);
}

View ttButton = findViewById(R.id.tt_button);
ttButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        scheduleTimer();
        nextActivity();
    }
});

为什么?
这里内存泄漏在于Timer和TimerTask没有进行Cancel,从而导致Timer和TimerTask一直引用外部类Activity。

怎么解决?
在适当的时机进行Cancel。

内存泄漏8:Sensor Manager

代码如下:
MainActivity.java

void registerListener() {
       SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
       Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
       sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}

View smButton = findViewById(R.id.sm_button);
smButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        registerListener();
        nextActivity();
    }
});

为什么?
通过Context调用getSystemService获取系统服务,这些服务运行在他们自己的进程执行一系列后台工作或者提供和硬件交互的接口,如果Context对象需要在一个Service内部事件发生时随时收到通知,则需要把自己作为一个监听器注册进去,这样服务就会持有一个Activity,如果开发者忘记了在Activity被销毁前注销这个监听器,这样就导致内存泄漏。

怎么解决?
在onDestroy方法里注销监听器。

总结

在开发中,内存泄漏*坏的情况是app耗尽内存导致崩溃,但是往往真实情况不是这样的,相反它只会耗尽大量内存但不至于闪退,可分配的内存少了,GC便会更多的工作释放内存,GC是非常耗时的操作,因此会使得页面卡顿。我们在开发中一定要注意当在Activity里实例化一个对象时看看是否有潜在的内存泄漏,一定要经常对内存泄漏进行检测。

求救,服务器一直频繁自动重启,怎么办

服务器是组装的,今天上午突然频繁自动重启,后面只有电源黄灯闪烁。检查了 raid 里面的硬盘都没有问题。到底是什么情况。求大神解密。

服务器 频繁 黄灯 raid8 条回复 • 2018-11-19 15:26:15 +08:00
benjix 1
benjix 2018-11-16 14:41:50 +08:00 via iPhone ❤️ 1
你是让大家猜吗?
dapang1221 2
dapang1221 2018-11-16 14:46:02 +08:00
大部分主板都带一串故障诊断的指示灯,8 位或 16 位的,2U 以上有的自带个显示故障代码的小屏幕,对着说明书查一下吧
lcatt 3
lcatt 2018-11-16 14:50:08 +08:00
重启一般和硬盘没关系, 首先看看 windows 操作系统事件日志,看看是软件问题还是硬件问题。如果是硬件问题,很可能是主板或者内存,通过服务器的监控软件可以导出,不清楚组装的主板是否提供这种工具。
gamexg 4
gamexg 2018-11-16 15:11:19 +08:00
@lcatt #3 +1

看看什么牌子的主板,找一下是否提供了自检光盘,或者主板是否自带了 lom 等功能。

服务器不太推荐 memtest 测试内存是否有故障。
上次一个 hp 服务器内存有问题,ilo 里面记录了不可恢复的内存故障造成了重启,但是跑了一夜 memtest 什么都没测试出来。
用 hp 光盘启动自检,几秒就确认内存存在问题,更换内存后故障解决。
huangzongzhuan 5
huangzongzhuan 2018-11-16 16:35:27 +08:00
换一台
CCNemo 6
CCNemo 2018-11-16 16:46:16 +08:00 via Android
运行过程中自动重启的话,可以看看系统内的日记,看看有什么蹊跷的,主板带诊断可以自检一下。
wisdom 7
wisdom 2018-11-17 13:34:36 +08:00 via iPhone
有一种东西叫检测卡
servers007 8
servers007 2018-11-19 15:26:15 +08:00
先查看下日志,看看是什么原因导致的,不能瞎猜啊,说换就换,家里有矿

韩国和日本有哪些靠谱的独服?

如题,希望可以提供原生日本或者韩国 ip 的独服,接受高价格,只是要保证到国内线路稳定 低延迟。

求各位指点迷津 谢过了

独服 韩国 日本 原生10 条回复 • 2019-01-05 10:18:51 +08:00
bestkayle 1
bestkayle 2018-11-28 08:56:18 +08:00 ❤️ 1
kdatacenter
xmlf 2
xmlf 2018-11-28 09:00:23 +08:00 via Android
@bestkayle 这家价格是挺高的
bestkayle 3
bestkayle 2018-11-28 09:06:49 +08:00
@xmlf #2 不过韩国的网络好像也会墙一些网站,一般是色情网站。
fl2d 4
fl2d 2018-11-28 09:33:06 +08:00
我用的 sakura
citydog 5
citydog 2018-11-28 09:47:19 +08:00 via Android
SK 的便宜
zhang1215 6
zhang1215 2018-11-28 10:03:01 +08:00
韩国有没有便宜的小鸡
QQ2171775959 7
QQ2171775959 2018-12-03 11:54:11 +08:00
目前我就知道九五相宜的还不错的。韩国的比较便宜点。但是回国内的日本的速度快一些。
Simcentric 8
Simcentric 2018-12-18 12:07:03 +08:00
这里 HK 三线回国低延迟高稳定,性价比高,要不要了解下?
carlp 9
carlp 2018-12-25 19:21:18 +08:00
日韩物理机比香港延时还是要稍大些。
靠谱是都靠谱,稳定,平价。
idcwk 10
idcwk 2019-01-05 10:18:51 +08:00
原生态韩国 ip,要排除广播的 ip 地址才行。可以叫我了解下。

C++ 中 Lambda 对变量的捕获居然是在声明时就做了

我看到在 C++ 14 特性中支持 Lambda 表达式捕获 move-only 的类型(原先是引用或值传递),于是写了一个小 demo:

auto p = std::make_unique<int>(1);
auto task1 = [p = move(p)](){ *p = 5; };
cout << *p << endl;
上述代码可以通过编译,但是运行时出现 core dump ,明显是因为此时 p 已经被 move 到 Lambda 表达式中去了,这里就迷惑,我一直以为要 auto task = [](){…}(); 或是 task1(); 执行时才会做参数的初始化(类似函数那样),但从结果来看,从声明的那一刻变量就已经被捕获了。

虽然这一点对原本的值传递和引用捕获的参数来讲没有什么感知,但是对于捕获 move-only 类型的函数确实会产生一定的影响,因为从 Lambda 声明的这一刻开始你原本的外部变量就不能使用了。

另外被捕获到 Lambda 内部的 move-only 变量是以类似 static 的状态存在的,即多次调用 task 对 *p 的改变会累积,以这个 demo 可以看的比较清楚:

auto p = std::make_unique<int>(1);
auto task1 = [p = move(p)](){
(*p)++;
cout << *p << endl;
};
task1();
task1();
task1();
task1 lambda auto 捕获6 条回复 • 2021-07-16 13:50:11 +08:00
wutiantong 1
wutiantong 1 天前 ❤️ 1
lambda 就是这样的,
你定义它时,你实际上是创建了一个匿名类型的 functor object,
而捕获列表中定义的都是这个匿名类型的成员变量,
所以,并不是 static 的,而是成员变量的。
nightwitch 2
nightwitch 1 天前 ❤️ 1
其实仔细想想就会发现你的思路不合理。
比如考虑一个 taskQueue,一个 lambda 会被推到这个队列里,而可能要等前面的任务都执行完毕以后才会执行。
如果 lambda 不在声明的时候拷贝 /移动变量,那么当 taskQueue 排到的时候可能这个变量生命周期早结束了。

lambda 其实是一个语法糖,本质上是声明一个重载了 operator()的 struct,auto task1 = []()… 类似于实例化这个结构体。
ipwx 3
ipwx 1 天前
楼主可能和 JS 搞起来了。但是 JS 那种其实不是更奇怪吗。

for (int i=0; i<10; ++i) {
task_list.emplace([i] () { std::cout << i << std::endl; });
}

这种用法不挺符合常识吗?
hitmanx 4
hitmanx 1 天前
“.. 但从结果来看,从声明的那一刻变量就已经被捕获了。虽然这一点对原本的值传递和引用捕获的参数来讲没有什么感知”..

Really?以值 capture 一样有感知

int main()
{
….int a = 1;
….auto func = [a]() { std::cout << a << std::endl; };
….a = 2;
….func();
….return 0;
}
mer 5
mer 1 天前
@wutiantong 原来是这样
@nightwitch 懂了
@ipwx 没用过 JS
@hitmanx 噢 我说的值传递不是这个意思,忘了考虑这种值 Capture 的,一般用 引用 capture 和,值传参
aneostart173 6
aneostart173 43 分钟前
跟 rust 一样啦?

前端是不是应该更注重性能优化,有什么大型前端项目学习?

目前在学前端,以为一直以为前端就是前端,现在深入了解之后发现其实前端也是部属在后端,就是前端所需的资源(代码 /样式 /资源)等都存储在后端的。只不过浏览器在打开的时候,从后端去获取信息再加载到浏览器中运行。

所以从这点来说,前端更加要注重性能:
1. 资源和数据要尽量小,方便快速传输加载。
2. 因为浏览器要开很多页面,所以前端资源应该尽早释放,要注重内存优化。
3. 前端已经不是单个静态网页,而是复杂的容器(可以做复杂的事情,比如 canvas,webgl )。

不知道大型前端项目的代码量有多少(应该也大不到哪里去,跟后端比就是小虾米)?
前端对构建大型应用的可能性有多大?
前端 后端 浏览器 资源21 条回复 • 2021-07-16 13:56:53 +08:00
murmur 1
murmur 1 天前 ❤️ 1
前端优化只能砍需求,而这个几乎没法实现,懒加载你的首页还是一堆乱七八糟的,懒也懒不到哪里去

一个清爽的首页无论怎么做都很优化,堆旧淘宝那对乱七八糟的东西在首页,怎么优化首屏都下不来
66beta 2
66beta 1 天前
(应该也大不到哪里去,跟后端比就是小虾米)
KisekiRemi 3
KisekiRemi 1 天前 ❤️ 1
原本以为是面向用户体验进行开发,现在才明白是面向领导和公司开发
rioshikelong121 4
rioshikelong121 1 天前
现在一个 tab 随随便便占几百 MB 内存。吓人。
CodeCodeStudy 5
CodeCodeStudy 1 天前 ❤️ 1
第 2 点不成立,浏览器开很多页面跟前端的工作没有什么关系,只要当前页面不卡顿就行,过度优化没有用
3dwelcome 6
3dwelcome 1 天前
“3. 前端已经不是单个静态网页,而是复杂的容器(可以做复杂的事情,比如 canvas,webgl )。”

老外有句话:选*合适的语言,做*合适的事情。

JS *好就是快速开发单页面应用,也别太在乎性能。你说搭建大型 webgl 应用,不是不可以,但对于 JS 来说,巨型项目代码管理压力真的还挺大。
maplerecall 7
maplerecall 1 天前 via Android
大型也得有个参照,举个例子,Bing 的前端部分代码 clone 下来大概 100 多 GB 。虽然包含了一些资源和测试文件,但其中无论整体框架还是业务代码的复杂度远超业界*大部分后端项目了。

另外随着 node 应用越来越广泛,现在前端工作其实已经混合了不少之前属于后端的内容。不同项目的偏重性也都不一样,并不能武断的说前端代码量就一定小。例如工具、编辑器以及*近流行的 lowcode 类项目的前端代码量很多都远超后端部分的。
James369 8
James369 1 天前
@maplerecall bing 前端是什么项目
SergeGao 9
SergeGao 23 小时 42 分钟前
@maplerecall 好奇 Bing 前端都包括了什么? 100GB 的前端项目还没见过。。
murmur 10
murmur 23 小时 38 分钟前
@maplerecall 因为 node 做后端就把后端强行算前端就没意思了,虽然大家都是全干,但是讨论的时候还是按浏览器和服务器划分

ericgui 23 小时 35 分钟前
能做的事越复杂, 性能就越差

所以你只能做平衡,在满足公司业务的需求的同时,别太慢了,就行了。
toesbieya 12
toesbieya 22 小时 40 分钟前
之前有看过有人说腾讯文档前端有上百万行代码
godblessumilk 13
godblessumilk 22 小时 28 分钟前 ❤️ 1
可以了解下 OHIF viewer 这个前端项目,一个在线的 DICOM 协议医疗图像查看器,涉及到了大量的 webGL 和 canvas 对二进制流的渲染以及图像处理,工程复杂度和代码量都远超常规小体积图像文本处理的后端,https://viewer.ohif.org/ 是个典型的 PWA 离线 web 应用,server worker 让 js 开了多线程提高性能
a4854857 14
a4854857 21 小时 59 分钟前
@godblessumilk 简单扫了一眼你的描述顺手打开还蛮吓人
CraxClive 15
CraxClive 19 小时 38 分钟前 via iPhone
@a4854857 的确吓人…
banricho 16
banricho 19 小时 34 分钟前 via Android
看方向,数据可视化、地图这种,或者石墨文档、Notion 这类都是比较复杂的前端业务。不要光看代码量,很多电商或管理后台只是页面多,但实际上难度并不大。
DiamondYuan 17
DiamondYuan 17 小时 12 分钟前 via iPhone ❤️ 3
推荐读 vscode 源码

1. 代码量大,有 50 万行
2. 用了大量设计模式,vscode 的开发者是 《设计模式》 这本书的作者。
3. 附带了 monaco,优秀的前端编辑器。(几乎所有 cloud ide 用的都是 monaco
4. 跨平台( mac windows linux,跨端 ( node electron web,可以了解到如何通过依赖注入屏蔽环境差异,如何组织代码
5. 自带插件系统,可以学习如何设计一套优秀的插件 api,学习如何进行进程隔离。
6. 主题:学习如何实现动态换肤
7. 语言服务(lap): 这个不做编辑器可以不看
8. src/vs/base 包含一系列基础库,可以拷贝到自己心目里直接用
blessyou 18
blessyou 17 小时 1 分钟前 via Android ❤️ 1
等一等,优化的前提是有监控或者数据对比。没有就只能瞎吹。
DiamondYuan 19
DiamondYuan 16 小时 57 分钟前 via iPhone
除了 vscode,还可以看 chrome 的 devtool (没错,这个是前端项目)。
yunyuyuan 20
yunyuyuan 4 小时 51 分钟前
优化是用来面试的