热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

内存优化(一)

一内存划分Android中的内存是由谁来管理?答案是JVM虚拟机——内存大管家。从线程的角度来看,JVM在执行Java程序的过程中会将所管理的内存划

一 内存划分

Android中的内存是由谁来管理?答案是JVM虚拟机——内存大管家。

从线程的角度来看,JVM在执行Java程序的过程中会将所管理的内存划分为线程私有和线程共有两大类。

1.1、线程私有

(1)程序计数器:Java是多线程的,既然是多线程就需要线程间切换、通信等操作。如何保证线程间操作时每个线程的执行顺序能按代码的步骤正常执行呢?这时候程序计数器就是关键了,它会帮我们记录当前线程执行到的位置(在字节码中记录的这些位置统称为指令地址)。这个内存区不会出现OOM

(2)虚拟机栈:我们常提在嘴上的堆和栈,其中栈指的就是Java虚拟机栈。每个线程在创建时都是创建一个对应的虚拟机栈,而虚拟机栈又有一个个的栈帧组成。每一个栈帧对应着一次方法调用。当前正在执行的方法对应的是当前栈帧。栈只会执行两种操作:压栈和出栈。栈帧中存储着对应方法的局部变量表,操作数栈,动态链接和方法的返回地址。即虚拟机栈中存储着栈帧,而每个栈帧里面存储着当前方法所需的数据、指令和返回等信息。这个内存区会出现栈溢出、OOM异常

(3)本地方法栈:这个和虚拟机栈作用类似,区别就是虚拟机栈为执行的Java服务,本地方法栈为Native服务

1.2、线程共有

(1)堆内存:Java虚拟机中最大的一块内存,主要目的就是存放对象实例和数组。也是Java虚拟机进行垃圾回收的主要工作区域。

(2)方法区:我们都知道Java中每个类都对应一个Class对象保存这个类的信息,方法区就是用来保存这个类信息的区域。同时它还会保存常量(包括运行时常量池)、静态变量以及编译器编译后的代码等。在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来

 二、内存管理—内存分配

Java中的内存时如何分配的?

2.1、堆内存区划分

我们这里所说的内存分配主要说的是堆内存块的分配。而要了解堆内存块的分配规则,我们要先了解JVM对堆内存块的区域划分。

JVM将堆内存进行了进一步的划分,如下:

●新生代(PSYoungGen):MinorGC。

新生代又细分成如下三个空间

  1)、Eden空间  

  2)、FromSurvivor空间

  3)、ToSurvivor空间

Eden空间用来优先分配对象、数组。From和To空间是个交换区。默认情况下这三个空间的内存占比是8:1:1。当然这个比例也是可以通过虚拟机调的。

●老年代(ParOldGen):Full GC

2.2、堆内存区的分配方式

我们创建一个对象时,JVM会去判断当前堆内存是否是规整状态。通过当前堆内存的规整状态来选择指针碰撞还是空闲列表方式进行分配。

指针碰撞:如果Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞”

空闲列表:如果Java堆中的内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”。选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
 

、内存管理—内存分配

前面我们也讲了,Java中的内存回收不由开发者来控制,而是由JVM虚拟机的垃圾回收器(GC)来进行回收管理。Java中GC是如何回收一个对象的?

3.1、GC如何判断一个对象可回收

1、引用计数法

  顾名思义当一个对象被另一个对象引用时,将该对象的引用计数+1。相反当该对象被引用对象释放时则-1。当这个对象的引用计数为0时,则认为这个对象是垃圾对象可回收。但是这种方法是不靠谱的,因为这种方法是无法判断一个对象是否真实无用可回收的。比如对象A引用了对象B,同时对象B也引用了A。虽然这两个对象在各自的引用者中都没有被用到,但是这个时候引用计数法就无法判断这个对象是否是可回收对象了。

2、可达性分析算法

  Java中可达性分析首先要确定Gc Roots对象。然后将所有与这个GCRoot对象有直接或间接引用立链的对象都统计为不可回收对象。

这个Gc Roots对象是个啥对象?所谓Gc Roots对象就是如下这些对象实列:

●方法区中类静态属性引用的对象,以及方法区中常量引用的对象

●虚拟机栈(本地变量表)中引用的对象

●本地方法栈(JNI开发Native方法)中引用的对象
 

3.2、GC如何回收内存 

在GC经过上述算法确定完那些对象可回收之后就是进行对象的回收操作了。但是回收操作是要根据不同的GC回收器的能力来完成的。而这个能力就是回收器的回收算法能力。

●复制回收算法:它的主要工作区域是年轻代

  它会将年代年轻的内存分出两块同样大小的区域(其实就是前面说的From、To两块内存区)。一块用来正常使用,一块空闲备用等待复制。判断完对象存活状态后,进行回收时,将存活对象复制到备用空间,然后清空之前的那块内存空间等待备用。复制回收算法的缺点就是内存只有50%利用率。这里就可以解释一下为什么前面的年轻代内存区占比是8:1:1了,在Java中我们通常认为有90%的对象是不需要回收的,只有10%的对象需要被回收,而复制回收算法需要预留一份与这个10%一样大小的内存区来备用。所以就得出了8:1:1的空间占比。复制回收算法就是在From和To之间进行内存Copy

标记-清除算法:主要作用域是老年代

  判断完内存区对象的存活状态后,会对垃圾对象做一个可回收的标记。等垃圾回收器扫描完所有内存后,一次性清除被标记为可回收的对象。标记算法的缺点是垃圾回收后内存空间是不连续的,存在内存碎片(也就是前面提到的内存不规整状态)。

标记-整理算法:主要作用域是老年代

 标记整理算法就是在标记清除算法的基础上解决了垃圾回收后内存碎片的问题,即清除垃圾对象后,会对内存区进行整理,使其成为规整状态。可是既然要整理内存区,就必然要进行内存移动,就会降低效率。所以它的效率比标记清除算法要低。


3.3 不同引用类型的回收状态

强引用

Object strongReference = new Object()

如果一个对象具有强引用,那垃圾回收器绝不会回收它,当内存空间不足, Java 虚拟机宁愿抛出 OOM 错误,使程序异常 Crash ,也不会靠随意回收具有强引用的对象来解决内存不足的问题.如果强引用对象不再使用时,需要弱化从而使 GC 能够回收,需要:

strongReference = null; //等 GC 来回收

还有一种情况,如果:

public void onStrongReference(){Object strongReference = new Object()
}

在 onStrongReference() 内部有一个强引用,这个引用保存在 java 栈 中,而真正的引用内容 (Object)保存在 java 堆中。当这个方法运行完成后,就会退出方法栈,则引用对象的引用数为 0 ,这个对象会被回收。

但是如果 mStrongReference 引用是全局时,就需要在不用这个对象时赋值为 null ,因为 强引用 不会被 GC 回收。

软引用 (SoftReference)

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

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收, java 虚拟机就会把这个软引用加入到与之关联的引用队列中。

注意: 软引用对象是在 jvm 内存不够的时候才会被回收,我们调用 System.gc() 方法只是起通知作用, JVM 什么时候扫描回收对象是 JVM 自己的状态决定的。就算扫描到了 str 这个对象也不会回收,只有内存不足才会回收。

弱引用 (WeakReference)

弱引用与软引用的区别在于: 只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

弱引用可以和一个引用队列联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。

可见 weakReference 对象的生命周期基本由 GC 决定,一旦 GC 线程发现了弱引用就标记下来,第二次扫描到就直接回收了。

注意这里的 referenceQueuee 是装的被回收的对象。

虚引用 (PhantomReference)

@Testpublic void onPhantomReference()throws InterruptedException{String str = new String("123456");ReferenceQueue queue = new ReferenceQueue();// 创建虚引用,要求必须与一个引用队列关联PhantomReference pr = new PhantomReference(str, queue);System.out.println("PhantomReference:" + pr.get());System.out.printf("ReferenceQueue:" + queue.poll());}

虚引用顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列 (ReferenceQueue) 联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

总结


引用类型调用方式GC是否内存泄漏
强引用直接调用不回收
软引用.get()视内存情况回收
弱引用.get()回收不可能
虚引用null任何时候都可能被回收,相当于没有引用一样

四、分析内存常用工具

工具很多,掌握原理方法,工具随意挑选使用。


top/procrank


meinfo


Procstats


DDMS


MAT


Finder - Activity


LeakCanary


LeakInspector


内存泄漏

产生的原因: 一个长生命周期的对象持有一个短生命周期对象的引用,通俗点讲就是该回收的对象,因为引用问题没有被回收,最终会产生 OOM。

下面我们来利用 Profile 来检查项目是否有内存泄漏


怎么利用 profile 来查看项目中是否有内存泄漏

1 在 AS 中项目以 profile 运行

 2 在 MEMORY 界面中选择要分析的一段内存,右键 export


  • Allocations: 动态分配对象个数

    Deallocation: 解除分配的对象个数

    Total count: 对象的总数

    Shalow Size: 对象本身占用的内存大小

    Retained Size: GC 回收能收走的内存大小

  • 转换 profile 文件格式

    • 将 export 导出的 dprof 文件转换为 Mat 的 dprof 文件

    • cd /d 进入到 Android sdk/platform-tools/hprof-conv.exe


3   下载 Mat 工具

4 打开 MemoryAnalyzer.exe 点击左上角 File 菜单中的 Open Heap Dupm

5 查看内存泄漏中的 GC Roots 强引用


Android 中常见的内存泄漏经典案例及解决方法

1 单例

示例 :

public class AppManager {private static AppManager sInstance;private CallBack mCallBack;private Context mContext;private AppManager(Context context) {this.mContext = context;}public static AppManager getInstance(Context context) {if (sInstance == null) {sInstance = new AppManager(context);}return sInstance;}public void addCallBack(CallBack call){mCallBack = call;}
}

  1. 通过上面的单列,如果 context 传入的是 Activity , Service 的 this,那么就会导致内存泄漏。

    以 Activity 为例,当 Activity 调用 getInstance 传入 this ,那么 sInstance 就会持有 Activity 的引用,当 Activity 需要关闭的时候需要 回收的时候,发现 sInstance 还持有 没有用的 Activity 引用,导致 Activity 无法被 GC 回收,就会造成内存泄漏

  2. addCallBack(CallBack call) 这样写看起来是没有毛病的。但是当这样调用在看一下勒。

    //在 Activity 中实现单例的回调
    AppManager.getInstance(getAppcationContext()).addCallBack(new CallBack(){@Overridepublic void onStart(){}
    });

    这里的 new CallBack() 匿名内部类 默认持有外部的引用,造成 CallBack 释放不了,那么怎么解决了,请看下面解决方法

解决方法:


  1. getInstance(Context context) context 都传入 Appcation 级别的 Context,或者实在是需要传入 Activity 的引用就用 WeakReference 这种形式。

  2. 匿名内部类建议大家单独写一个文件或者

    public void addCallBack(CallBack call){WeakReference mCallBack= new WeakReference(call);}


2 Handler

示例:

//在 Activity 中实现 Handler
class MyHandler extends Handler{private Activity m;public MyHandler(Activity activity){m=activity;}// class.....
}

这里的 MyHandler 持有 activity 的引用,当 Activity 销毁的时候,导致 GC 不会回收造成 内存泄漏。

解决方法:

1.使用静态内部类 + 弱引用
2.在 Activity onDestoty() 中处理 removeCallbacksAndMessages() @Overrideprotected void onDestroy() {super.onDestroy();if(null != handler){handler.removeCallbacksAndMessages(null);handler = null;}}

3 静态变量

示例:

public class MainActivity extends AppCompatActivity {private static Police sPolice;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);if (sPolice != null) {sPolice = new Police(this);}}
}class Police {public Police(Activity activity) {}
}

这里 Police 持有 activity 的引用,会造成 activity 得不到释放,导致内存泄漏。

4 非静态内部类

参考 第二点 Handler 的处理方式

5 匿名内部类

示例:

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread(){@Overridepublic void run() {super.run();}};}
}

很多初学者都会像上面这样新建线程和异步任务,殊不知这样的写法非常地不友好,这种方式新建的子线程ThreadAsyncTask都是匿名内部类对象,默认就隐式的持有外部Activity的引用,导致Activity内存泄露。

解决方法:

//静态内部类 + 弱引用
//单独写一个文件 + onDestory = null;

6 未取消注册或回调

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);registerReceiver(mReceiver, new IntentFilter());}private BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {// TODO ------}};
}

在注册观察则模式的时候,如果不及时取消也会造成内存泄露。比如使用Retrofit + RxJava注册网络请求的观察者回调,同样作为匿名内部类持有外部引用,所以需要记得在不用或者销毁的时候取消注册。

解决方法:

//Activity 中实现 onDestory()反注册广播得到释放@Overrideprotected void onDestroy() {super.onDestroy();this.unregisterReceiver(mReceiver);}

7 定时任务

public class MainActivity extends AppCompatActivity {/**模拟计数*/private int mCount = 1;private Timer mTimer;private TimerTask mTimerTask;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();mTimer.schedule(mTimerTask, 1000, 1000);}private void init() {mTimer = new Timer();mTimerTask = new TimerTask() {@Overridepublic void run() {MainActivity.this.runOnUiThread(new Runnable() {@Overridepublic void run() {addCount();}});}};}private void addCount() {mCount += 1;}
}

当我们Activity销毁的时,有可能Timer还在继续等待执行TimerTask,它持有Activity 的引用不能被 GC 回收,因此当我们 Activity 销毁的时候要立即cancelTimerTimerTask,以避免发生内存泄漏。

解决方法:

//当 Activity 关闭的时候,停止一切正在进行中的定时任务,避免造成内存泄漏。private void stopTimer() {if (mTimer != null) {mTimer.cancel();mTimer = null;}if (mTimerTask != null) {mTimerTask.cancel();mTimerTask = null;}}@Overrideprotected void onDestroy() {super.onDestroy();stopTimer();}

8 资源未关闭

ArrayList,HashMap,IO,File,SqLite,Cursor 等资源用完一定要记得 clear remove 等关闭一系列对资源的操作。

解决方案 :用完就释放

9 属性动画

动画同样是一个耗时任务,比如在 Activity 中启动了属性动画 (ObjectAnimator) ,但是在销毁的时候,没有调用 cancle 方法,虽然我们看不到动画了,但是这个动画依然会不断地播放下去,动画引用所在的控件,所在的控件引用 Activity ,这就造成 Activity 无法正常释放。因此同样要在Activity 销毁的时候 cancel 掉属性动画,避免发生内存泄漏。

解决方案 :销毁的时候取消动画

10 由于webview存在内存系统泄漏,还有图库内存过多的问题

解决方案:webview采用独立的进程

11 Android 源码或者第三方 SDK

//如果在开发调试中遇见 Android 源码或者 第三方 SDK 持有了我们当前的 Activity 或者其它类,那么现在怎么办了。

解决方法: //当前是通过 Java 中的反射找到某个类或者成员,来进行手动 = null 的操作。


内存抖动

内存抖动是由于短时间内有大量对象进出新生区导致的,它伴随着频繁的GC。 gc会大量占用ui线程和cpu资源,会导致app整体卡顿

android profile 效果图如下图

 我们可以看到 上面的一溜白色垃圾桶。说明在大量的执行gc操作。用了一会儿 手机就开始卡了

我们就看一下 源代码的样子


public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {imPrettySureSortingIsFree();}});}/***  排序后打印二维数组,一行行打印*/public void imPrettySureSortingIsFree() {int dimension = 300;int[][] lotsOfInts = new int[dimension][dimension];Random randomGenerator = new Random();for (int i = 0; i

发现 rowAsStr 对象在被不断地创建。 我们可以把它优化一下


public class MainActivity extends AppCompatActivity {&#64;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {&#64;Overridepublic void onClick(View v) {imPrettySureSortingIsFree();}});}/***  排序后打印二维数组&#xff0c;一行行打印*/public void imPrettySureSortingIsFree() {int dimension &#61; 300;int[][] lotsOfInts &#61; new int[dimension][dimension];Random randomGenerator &#61; new Random();for (int i &#61; 0; i // rowAsStr &#43;&#61; sorted[j];sb.append(sorted[j]);if(j <(lotsOfInts[i].length - 1)){
// rowAsStr &#43;&#61; ", ";sb.append(", ");}}rowAsStr &#61; sb.toString();Log.i("ricky", "Row " &#43; i &#43; ": " &#43; rowAsStr);}}public int[] getSorted(int[] input) {int[] clone &#61; input.clone();Arrays.sort(clone);return clone;}}

看优化后的

 这里可以看见没有垃圾桶出现&#xff0c;说明内存抖动解决了。

下面是避免发生内存抖动的几点建议&#xff1a;


  • 尽量避免在循环体内创建对象&#xff0c;应该把对象创建移到循环体外。
  • 注意自定义View的onDraw()方法会被频繁调用&#xff0c;所以在这里面不应该频繁的创建对象。
  • 当需要大量使用Bitmap的时候&#xff0c;试着把它们缓存在数组中实现复用。
  • 对于能够复用的对象&#xff0c;同理可以使用对象池将它们缓存起来。


 


推荐阅读
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • “你永远都不知道明天和‘公司的意外’哪个先来。”疫情期间,这是我们最战战兢兢的心情。但是显然,有些人体会不了。这份行业数据,让笔者“柠檬” ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
author-avatar
手机用户2502940165
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有