我们学习程序语言一定不要被束缚到语言细节和规范上面,而要从计算机逻辑执行层面思考。细节和规范都是人为设定的,是大牛抽象计算机逻辑后的加工品。对于技术问题我们要寻根溯源,要高屋建瓴。

这段时间在准备面试,为了自认为更好的表现而去背大量的面试题。今日在知乎看到了这一段话,让自己醍醐灌顶。

我们就从java虚拟机这个方面开始说说看,很多小白一上来就被各种分代,处理器类型搞晕了包括我,往往就是死记硬背了那么一些规则,根本谈不上理解的深入。但是事情的出现都是有原因的,都是为了解决实际的问题。

java程序运行在虚拟机之上,虚拟机又和操作系统打交道,完成数据的存储,运算,输出。虚拟机在加载.class文件的时候会在内存开辟一个“方法区”来存储类的基本信息同时在“堆”生成一个Class对象为反射提供了支持。。内存是有限的,而程序运行时又不断的创建对象,因此我们要定期处理一些不用的对象。生成对象的方法有new,反射,清理对象就有我们常用的GC算法如标记,标记整理,复制算法等等。

为什么会有三种方法?因为每种方法都有自己的局限性,标记-清除算法的缺点在于会产生大量的内存碎片标记-整理算法则是对它的一种改进,将碎片往一边移动,缺点是。复制算法则解决了内存碎片的问题,但浪费了50%的额外空间。我们要依据不同类型对象的特点采取最合适的算法进行处理。对于新生代,由于数量少我们采用复制算法提高效率,对于老生代对象因为数量众多我们采取标记-整理算法进行处理。

好了具体垃圾回收算法我们清除了,我们再想想按什么策略去执行垃圾回收?大多数人都能想到的是后台运行一个进程,每隔段时间进行垃圾回收,完成之后再继续执行原来的程序,这就是最原始的串行回收器Serial。多核的出现人们自然想到使多个线程同时执行垃圾回收的工作,减少垃圾回收时间如ParNew,为了将啊垃圾回收对程序的影响(STW)降到最低,人们又发明了并发回收器(CMS),仍需要STW。最新的G1收集器与CMS一大优势就是建立可预测的回收时间,指明在长度为M毫秒的时间段内,垃圾回收的时间不超过N秒。

再看看多线程,我们都知道线程切换会消耗资源,那么具体消耗什么资源呢?从计算机底层来讲,操作分三步,从内存取值,计算,将值放入内存。大概过程就是PC(程序计数器)从指存(cache)取下一条指令,IR(指令寄存器)保存当前执行的指令,再从数存取数据结合ALU(算术逻辑单元)通用寄存器完成计算后将结果放入DR(数据缓存寄存器),最后将DR的值放入内存。我们想一下为什么要这么设计(不要死记硬背),cache是缓存层可以加快取指令的速度,执行程序时我们当然需要保存现在的指令(IR)和下一条指令(PC),指令有了我们需要数据吧,那就从数存(AR数据地址寄存器)里面取,指令数据都有了我们要执行具体的运算那么就用到了ALU,但是不是还差了什么?比如1+1,这两个1放到那里,运算结果2又该放那里,这就需要一个工作区(通用寄存器)存放运算间的数据。运算完成了我们需要输出结果,输出到那里?对,就是DR,最后进入内存。为什么不直接输入到内存,这和取值需要cache一个道理,补偿CPU和内存在速度上面的差别。刚开始学组成原理各种寄存器给我留下了莫大的阴影,但是一切的设计背后的思路都是很简单了,各种细节与规定是为了保证实际操作的安全性和可靠性,所以我们首先要去理解设计后面的思想而不要将自己局限于细节。扯的有点远了,再看看多线程切换为什么消耗资源。java虚拟机中的程序计数器保存着当前线程执行的指令,切换线程时就要把线程的执行指令放到操作系统的指令寄存器,除此之外线程的其它资源也需要切换比如I/O设备。除了切换,线程的创建也是需要消耗资源的。


诗和远方丶
24 声望4 粉丝

每篇都是原创,拒绝复制粘贴


« 上一篇
设计模式
下一篇 »
Java 并发编程