jQuery选择器(Selector)总结

最喜欢jQuery网站上的一句话”Write Less, Do More”。除了有非常丰富的函数可以选用以外,它的选择器也是非常强大,确实大大的提高的生产效率。今天就把这些有用的选择器好好总结一下。

jQuery的选择器可以归为三类:基本CSS选择器位置选择器还有自定义选择器

  • 基本选择器又被称为“查找选择器”,用来找到DOM中的各种元素。
  • 位置和自定义选择又被称为“过滤选择器”,因为它可以过滤出一个元素的集合。

下面就分类看看这几种选择器的使用方法。

基本CSS选择器(Basic CSS Selectors)

基本选择器的很多语法很像CSS,很容易记忆。

[table id=1/]

位置选择器(Positional Selectors)

这类选择器是基于元素之间位置关系的,而且可以和任意的基础选择器一起使用。

[table id=2/]

自定义选择器(Custom Selectors)

[table id=3/]

使用jQuery获得Input的值

jQuery可以非常方便的取到网页里标准input的值。我们主要使用val()方法,用几个例子说明一下。

Text Input

HTML

JavaScript

Select Input

HTML

JavaScript

selectedVal可能是option_one/option_two/option_three.

当多选的情况时,我们可以得到所有selected items的value的数组,如:

HTML

JavaScript

CheckBox Input

HTML

JavaScript

如果没有Box selected, checkValue是一个空串。

Radio Input

HTML

JavaScript

 

JVM的Garbage Collection(GC)

垃圾回收把程序员从繁重的内存管理的任务中解放出来,提高生产效率。我以前做C++的时候就对Java的这个特性非常感兴趣,今天结合书的参考资料,深入地研究一下GC中的原理和工作机制。(以Hotspot JVM为例)

关于对象的“生死”

当我们在Heap上分配了内存给对象实例后,JVM怎么判断某个对象已经无用可以进行回收了呢?答案是:JVM会采用根搜索算法找出所用不可达的对象,对它们进行标记。(JVM没有使用C++里智能指针常用的引用计数法。)

标记一般会做两次,第一次标记是找出所有和GC Roots不相连的对象,然后判断些对象是否有必要执行finalize()(如果对象没有覆盖finalize()或者finalize()已经被JVM调用过,则不会执行。finalize()只会被执行一次。)。如果判定为有必要执行finalize(),那对象会被放置在一个名为F-Queue的队列中。在稍后由一条JVM自动建立的、优先级很低的Finalizer线程中执行(这就是为什么常常看到finalize()执行和gc()不在一起)。原因是为了避免finalize()方法中有执行缓慢的操作。然后JVM会对F-Queue进行一次小规模的第二次标记。如果对象不能在finalize()中逃出升天,那就是真要的“死了”(被JVM回收掉)。

方法区上的回收

堆上对象生死清楚之后,看看方法区。方法区只要收集两部分内存:无用的常量和无用的类。无用常量的判断很容易和对象很像,无用的类麻烦了些。类要满足3个条件才能算是“无用的”。

  • 该类所有的实例都已经被回收。
  • 加载该类的ClassLoader已经被回收。
  • 该类对应的Class类没有在任何地方被引用 ,无法在任何地方通过反射访问该类的方法。
我们可以用一些JVM参数监控类的回收与加载(-Xnoclassgc, -verbose:class, -XX:+TraceClassLoading, -XX:+TraceClassUnLoading)等等。

垃圾回收的算法

知道了回收的对象,也就确定了垃圾的内存,下来看几种具体回收的算法。

1. 标记-清除算法(Mark-Sweep)

此算法就是先对垃圾内存进行标记,然后再清除掉。这个算法有两个缺点,一个是效率,一个是清除之后会生产很多内存碎片。

2. 复制算法(Copying)

为了解决效率问题,复制算法出现了。它将内存分为大小相等的两块,每次只使得其中一块。当这块内存用完了就将活着的对象复制到另一块上。然后再把已使用过的内存一次清理掉。这样就不用考虑内存碎片的问题了。

能选用这个算法有一个统计的前提。IBM的研究表明,新生代的对象98%是朝生夕死(但这不是说新生代的对象也就是说可以准备两块内存,但一块大一块小,比如10:1或8:1。平时使用大的,一段时间后,大内存块中大部分内存死去,把活的复制到小内存块上,实现了高效的回收。那块小内存就像是一块分配担保的空间。

3. 标记-整理算法(Mark-Compact)

复制算法有一个问题就是会浪费内存,且当如果接近100%的内存(如老年代)都存活时,复制算法就无法发挥它的优势。 于是在Mark-Sweep的之上,提出了另一种算法。在标记之后,让所有活的对象都向内存块的一端移动,再清理掉边界的内存。这样就不会有碎片了。

4. 分代收集算法(Generational Collection)

所谓“代”就是根据对象存活的周期划分出来的。Java堆分为新生代和老年代(听说C#分4代)。根据不同代的特点,采用不同的算法进行GC就是分代收集了。

  • 对于新生代,每次会有大批对象死去,少量存活,就选用复制算法。
  • 对于老年代,对象存活概率大,没有额外空间做担保(也没有必要),必须使用“标记-清理”或“标记-整理”算法进行回收。

垃圾回收器(Garbage Collector)

HotSpot中实做了很多不同的Garbage Collector,作用在不同的“代”上。

新生代上的垃圾回收器

1. Serial收集器

据说是最基本、历史最悠久的收集器,它是一个单线程的收集器,更可怕的是,它在执行时,会终止掉所有的用户线程,又叫“Stop the world!“。虽然每次世界只停顿短短的几ms.

回收算法采用的是复制算法

2. ParNew收集器

它是Serial收集器的多线程版本(在新生代上),其余的行为、规则和策略都与Serial完全相同。

3. Parallel Scavenge收集器

它和ParNew一样,使用复制算法的并行多纯种收集器。但它有一个特别之处。它的目标是达到一个可控制的吞吐量(Throughput)。吞吐量就是CPU用于用户代码的时间与CPU总消耗时间的比值。可见高吞吐量可以最高效的利用CPU时间,尽快的完成程序的运算任务。Parallel Scavenge收集器有两个参数用于精确控制吞吐量,分别是-XX:MaxGCPauseMillis和-XX:GCTimeRatio.

MaxGCPauseMillis参数允许的值是一个大于0的毫秒数,收集器将尽力保证内在回收花费的时间不超过设定值。GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的。

GCTimeRatio参数的值是一个大于0小于100的参数,也就是垃圾收集时间占总时间的比率,相当于吞吐量的倒数。

4. CMS (Concurrent Mark Sweep) 收集器

它的目标是获取最短回收停顿时间。它是一使用的Mark-Sweep算法,分为4个步骤,包括:

  • 初始标记
  • 并发标记
  • 重新标记
  • 并发清除

 

它的优点就是并发收集、低停顿。但它也存在一些缺点:

  • CMS对CPU资源非常敏感。
  • CMS收集器无法处理浮动垃圾。
  • 使用Mark-Sweep算法,效率低。当碎片过多时,就会触发Full-GC。

总结

本文是对JVM垃圾回收的一个概括性的总结。

探索一下Java的引用类型

Java在java.lang.ref中提供了很多reference类型,包括软引用(SoftReference), 弱引用(WeakReference)还有虚引用(PhantomReference)。JVM的垃圾回收器对不同的引用类型有不同的行为,我试着深入探索一下。

探索之前,让我先引入一个类。

非常简单,实例化BigObj大约需要分配1M左右的堆内存;有一个自己的ID;一个自己的方法。

强引用(Strong Reference)

在讲其他引用之前,要说一下强引用。其实强引用就我们一般定义的引用,如:

aObj就是强引用类型,它指向堆中的对象实例。想让JVM回收这个对象,就是去掉所有指向该对象的强引用就可以了。这也是我们大家所熟知的。

执行它(加上-verbose:gc), 我们就可以看到,

第一次GC时,还需要1M左右的内存;但在aObj=null之后,再次GC时,大约有1M的内存被回收了。

软引用(Soft Reference)

下面来看看什么是软引用,看看下面的代码。

和强引用比起来,可能有点不太习惯。首先构造软引用需要给一个强引用,在强引用设置成null时,softObj.get()仍可正常工作。其实你可以认为它就比强引用复杂了一点,要用get()方法取回实例对象。再看,

运行结果,

把softObj置成null, GC做回收。

好像一切都和强引用差不多。但它不止这么简单,还有一个不一样的地方,在内存紧张时(快OutOfMemory时)它也会被回收,所以它比较适合做某种缓存。

让我们实验一下,

我们有一个List,里面全是BigObj对象。执行一下(加上-Xms5m -Xmx5m 设置Heap的大小),结果是,

在内存快耗尽时,JVM尝试回收SoftReferece指向的内存,虽然最后还是耗尽了。有几点要注意,首先List里的都是强类型,所以它不会被GC回收;还要注意一下,

如果没有这一行,BigObj(0)就有一个强引用指向它,因此JVM也不会回收它,这也就失去了SoftReference原有的作用。

弱引用(Weak Reference)

然后是弱引用,基本用法和Soft差不多(如get()),但它真的比Soft还要弱。真接看Code,

运行结果,

可见,在失去所有强引用时,WeakReference也会被回收。

虚引用(PhantomReference)

首先名字很酷,为什么叫虚呢?因为它的get()方法永远返回null。看看Code,

执行结果(-Xms2m -Xmx2m):

结果说明,Phantom和Soft有一样的特点,不会随着主引用不变化而发生GC,主有在内存吃紧时,才会有GC动作。

此外这里还有个新东西,就是ReferenceQueue。这是PhantomReference存在的原因。

引用队列(ReferenceQueue)

其实前面三种引用类型的构造函数都可以接收ReferenceQueue,但PhantomReference是强制需要的。如果使用了它,在堆对象释放之前,它被被放到ReferenceQueue里。这使我们能够在堆对象被回收之前采取行动。

结果:

看了一些文章,感觉不同的JVM对于这几种Reference的支持还是不太一致的。

一些参考文献

  1. http://www.ibm.com/developerworks/cn/java/j-lo-langref/
  2. http://www.ibm.com/developerworks/cn/java/j-refs/

 

这个ArrayList不是那个ArrayList

早上同事发现了一个很有趣的问题,后来详细跟踪了一下,发现了原因。

有这样的一段Java Code,

看上去很好,但会有个异常等着你,是UnsupportedOperationException.

为什么呢?Arrays.asList会返回一个ArrayList, 但注意,这个ArrayList不是那个ArrayList。它是Arrays的一个Nested Class,所以应该说它是Arrays.ArrayList. 而且它没有实现add, remove等接口,所以那个异常就出来了。

这个例子告诉我们Arrays.asList生成的List是不能修改的,是一个AbstractList。