概述

jdk1.2+就提供了四种类型的引用:强引用、软引用、弱引用和虚引用。这四种引用被回收的容易程度由低到高依次为:强引用、软引用、弱引用和虚引用。

Java中提供这四种引用类型主要有两个目的:第一是可以让程序员通过代码的方式决定某些对象的生命周期;第二是有利于JVM进行垃圾回收。

各种引用的java实现

软引用:通过java.lang.ref.SoftReference类来包装对象

 SoftReference<String> sr = new SoftReference<String>(new String("hello"));

弱引用:java.lang.ref.WeakReference类来包装对象

WeakReference<String> sr = new WeakReference<String>(new String("hello"));

虚引用:通过java.lang.ref.PhantomReference类来包装对象

ReferenceQueue<String> queue = new ReferenceQueue<String>();//虚引用必须和引用队列关联使用
PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);

强引用:不使用以上引用方式创建的对象都属于强引用

使用场景

  • 强引用:除非对象没有了引用,否则不会被gc回收。如将对象的引用赋值为null,这样JVM在合适的时间就会回收该对象。

  • 软引用:当内存空间不足了,就会回收这些对象的内存。软引用可用来实现内存敏感的高速缓存。

  • 弱引用:当gc进行扫描内存时,一旦发现有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。Weak引用对象常常用于Map数据结构中,以及适合缓存占用内存空间较大但可以随时释放的对象。

  • 虚引用:当gc进行扫描内存时,如果发现有虚引用的对象,先把这个对象放到与之关联的引用队列中,直到引用队列被处理才回收队列里的对象。虚引用主要用来跟踪对象被垃圾回收器回收的活动

WeakHashMap

WeakHashMap的存储结构类似于HashMap,内部是通过弱键key来管理 Entry 的,当调用WeakHashMap的方法的时候就会把 不用的Entry(就是key没有被强引用) 释放掉并把Entry.value赋值为null,以便被GC回收。WeakHashMap适合短时间内就过期的缓存场景。

注意点

  1. 通过System.gc()/Runtime.getRuntime().gc() 来通知JVM进行垃圾回收,但是要注意的是,虽然发出了通知,JVM不一定会立刻执行垃圾回收。

  2. 软引用、弱引用允许和一个引用队列联合使用,如果所引用的对象被垃圾回收,Java虚拟机就会把这个对象加入到与之关联的引用队列中,以便对这些被清除的弱引用对象进行统一管理。

参考资料

  1. Java 如何有效地避免OOM:善于利用软引用和弱引用

概要

会话是用来确定这个请求是属于哪个用户的,Web应用程序是使用HTTP协议传输数据的,而HTTP协议是无状态的协议,因此就需要跟踪会话来弥补,常用的会话跟踪技术是Cookie与Session。

Cookie实际上是一小段的文本信息,为了记录用户在一段时间内访问 Web 应用的行为路径,存储在客户端上,但是每次客户端的访问都必须传回这些 Cookie,如果 Cookie 很多,这无形地增加了客户端与服务端的数据传输量,而 Session 的出现正是为了解决这个问题。

不同的浏览器采用不同的方式保存Cookie。 IE浏览器会在“C:\Documents and Settings\你的用户名\Cookies”文件夹下以文本文件形式保存,一个文本文件保存一个Cookie。

Cookie的工作原理

客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。

Session的工作原理

当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 “session id”,如果已包含一个”session id”则说明以前已经为此客户端创建过session,服务器就按照”session id”在session表把这个 session检索出来使用,如果客户端请求不包含”session id”,则为此客户端创建一个session(注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session)并且生成一个与此session相关联的”session id”,这个”session id”将被在本次响应中返回给客户端保存。之后每当用户继续访问,如果Session没到期则服务器就会更新Session的最后访问时间,并维护该Session。

cookie和session的区别

Cookie通过在客户端记录的信息确定用户身份,Session通过在服务器端记录的信息确定用户身份。 Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力。

cookie和session的联系

服务器向客户端浏览器发送一个名为JSESSIONID的Cookie,它的值为该Session的id(也就是HttpSession.getId()的返回值)。Session依据该Cookie来识别是否为同一用户。

如果客户端浏览器将Cookie功能禁用,或者不支持Cookie时可以使用“URL地址重写”(在URL结尾加上会话ID标识参数和值,服务器通过会话ID识别不同用户)或隐藏表单域(将会话ID埋入HTML表单隐藏域提交到服务端。

cookie注意点

  • Cookie功能需要浏览器的支持。如果浏览器不支持Cookie(如大部分手机中的浏览器)或者把Cookie禁用了,Cookie功能就会失效。

  • Cookie在客户端是由浏览器来管理的,而浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名。Google与Baidu的域名不一样,因此Google不能操作Baidu的Cookie。如:JavaScript只能够任意地读写同一个域名下的Cookie

  • Cookie内容不宜过多,否则影响请求处理速度。

Session的注意

  • Session保存在服务器端。为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此,Session里的信息应该尽量精简。

  • Session 的致命弱点是不容易在多台服务器之间共享,所以这也限制了 Session 的使用。

cookie的api 简述

Java中把Cookie封装成了javax.servlet.http.Cookie类。通过request.getCookie()获取客户端提交的所有Cookie(以Cookie[]数组形式返回),通过response.addCookie(Cookiecookie)向客户端设置Cookie。

Session的api 简述

Session对应的类为javax.servlet.http.HttpSession类。Session也是一种key-value的属性对,通过getAttribute(String key)和setAttribute(String key,Object value)方法读写客户状态信息。Servlet里通过request.getSession()方法获取该客户的Session。

额外

1.一致性分布式Session的解决:提供分布式缓存来存储和写入Session,从而减轻服务端的内存压力。另外,为了保证一些应用对 Session 稳定性的特殊要求可以将一些非常关键的 Session 再存储到客户端 Cookie 中,如当分布式缓存存在问题时,因为Cookie中有部分关键的Session数据,这样即使分布式缓存出现问题也不会影响关键业务的正常运行。

2.如果在一个域名下已经登录成功,如何访问到另外一个域名的应用且保证登录状态仍然有效?这就需要实现 Session同步,需要另外一个跳转应用,这个应用可以被一个或者多个域名访问,它的主要功能是从一个域名下取得 sessionID,并通过Cookie同步到另外一个域名下。

参考资料

  1. 如何区分不同用户——Cookie/Session机制详解

  2. Android与WEB服务器交互时,如何保证在同一个会话Session中通信

  3. 深入理解 Session 与 Cookie

概述

设计模式使用场景 一文中已经可以知道设计模式的使用场景了,那么和设计模式离不开的设计原则呢?在文本将主要描述六大设计原则的实现。

1.“开-闭”原则(OCP)

OPEN CLOSE PRINCIPLE:一个软件实体(类和功能方法)应当不修改现有代码的基础上,引入新功能。

  • 多使用抽象类和接口

对于具有相似行为的类进行抽象化处理,将公共的部分放到抽象父类中,当需要扩展的时候,只需要实现一个具体的子类即可。

对于没有公共的部分,可以设定一个处理接口,当需要扩展的时候,只需要子类实现该接口来实现不同的行为。

  • 封装变化部分

尽可能地将会发生变化的内容独立出来,单独封装,而将这一部分作为抽象为其它部分调用。这样一旦有变化的需求,只需要修改或者更新封装的部分即可适应整体的变化,大大减少了变化的内容。

2.单一职责原则(SRP)

Single Responsibility Principle:不要让类实现过多的功能点,最好一个类一个功能点,以确保一个实现类只有一个引起它变化的原因。

3.依赖倒换原则(DIP)

Dependence Inversion Principle:在类的实例变量声明中应该尽量使用接口作为对象实例变量类型,这样就将类和具体实现之间解耦了

4.接口隔离原则(ISP)

Interface Segregation Principle:如果把接口比作角色,那么不同的角色分别代表不同的接口,且接口中的方法都只能属于这个角色的

5.里氏替换原则(LSP)

Liskov Substitution Principle:使用抽象类或接口作为方法的参数类型,然后将具体的子类型作为实际运行的参数传入,实现具体的行为处理

6.迪米特法则(LOD)

Low Of Demeter:尽量不要让类和类之间建立直接的关系,如果需要建立关系,最好通过其友元类来中转建立关系。但也带来的缺点就是可能存在大量的中介者类充当中转的角色,造成类的膨胀以及系统处理的复杂度。所以,在实际中还是要权衡使用LOD法则

参考资料

  1. 一本好书《软件秘笈 设计模式那点事》