JAVA知识点-HTTP状态码、HashMap实现原理、简单了解分代回收机制

本文最后更新于:May 2, 2022 am

积土成山,风雨兴焉;积水成渊,蛟龙生焉;积善成德,而神明自得,圣心备焉。故不积跬步,无以至千里,不积小流无以成江海。齐骥一跃,不能十步,驽马十驾,功不在舍。面对悬崖峭壁,一百年也看不出一条裂缝来,但用斧凿,能进一寸进一寸,能进一尺进一尺,不断积累,飞跃必来,突破随之。

目录

常见的HTTP协议状态码

常见的状态码有:

1xx代表服务器端已经接受了请求。

2xx代表请求已经被服务器端成功接收,最常见的有200、201状态码。

3xx代表路径被服务器端重定向到了一个新的URL,最常见的有301、302状态码。

4xx代表客户端的请求发生了错误,最常见的有401、404状态码。

5xx代表服务器端的响应出现了错误。

拓展

HTTP状态码(HTTP Status Code)是用来表示超文本传输协议响应状态的3位数字代码,所有状态码的第一个数字代表了响应的五种状态之一,所示的消息短语是典型的,但是可以提供任何可读取的替代方案。

1xx:

指定客户端相应的某些动作,代表请求已被接受,需要继续处理。由于 HTTP/1.0 协议中没有定义任何 1xx 状态码,所以除非在某些试验条件下,服务器禁止向此类客户端发送 1xx 响应。

2xx:

代表请求已成功被服务器接收、理解、并接受。这系列中最常见的有200、201状态码。

  • 200(成功):服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。
  • 201(已创建):请求成功并且服务器创建了新的资源。
  • 202(已接受):服务器已接受请求,但尚未处理。
  • 203(非授权信息):服务器已成功处理了请求,但返回的信息可能来自另一来源。
  • 204(无内容):服务器成功处理了请求,但没有返回任何内容。
  • 205(重置内容):服务器成功处理了请求,但没有返回任何内容。
  • 206(部分内容):服务器成功处理了部分 GET 请求。

3xx:

代表需要客户端采取进一步的操作才能完成请求,这些状态码用来重定向,后续的请求地址(重定向目标)在响应头Location字段中指明。这系列中最常见的有301、302状态码。

  • 300(多种选择):针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。
  • 301(永久移动):请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
  • 302(临时移动):服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
  • 303(查看其他位置):请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
  • 304(未修改):自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
  • 305(使用代理):请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理。
  • 307(临时重定向):服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。

4xx:

表示请求错误。代表了客户端看起来可能发生了错误,妨碍了服务器的处理。常见有:401、404状态码。

  • 400(错误请求):服务器不理解请求的语法。
  • 401(未授权):请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。
  • 403(禁止):服务器拒绝请求。
  • 404(未找到):服务器找不到请求的网页。
  • 405(方法禁用):禁用请求中指定的方法。
  • 406(不接受):无法使用请求的内容特性响应请求的网页。
  • 407(需要代理授权):此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。
  • 408(请求超时):服务器等候请求时发生超时。
  • 409(冲突):服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。
  • 410(已删除):如果请求的资源已永久删除,服务器就会返回此响应。
  • 411(需要有效长度):服务器不接受不含有效内容长度标头字段的请求。
  • 412(未满足前提条件):服务器未满足请求者在请求中设置的其中一个前提条件。
  • 413(请求实体过大):服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。
  • 414(请求的 URI 过长):请求的 URI(通常为网址)过长,服务器无法处理。
  • 415(不支持的媒体类型):请求的格式不受请求页面的支持。
  • 416(请求范围不符合要求):如果页面无法提供请求的范围,则服务器会返回此状态代码。
  • 417 (未满足期望值):服务器未满足”期望”请求标头字段的要求。

5xx:

代表了服务器在处理请求的过程中有错误或者异常状态发生,也有可能是服务器意识到以当前的软硬件资源无法完成对请求的处理。常见有500、503状态码。

  • 500(服务器内部错误):服务器遇到错误,无法完成请求。
  • 501(尚未实施):服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。
  • 502(错误网关):服务器作为网关或代理,从上游服务器收到无效响应。
  • 503(服务不可用):服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。
  • 504(网关超时):服务器作为网关或代理,但是没有及时从上游服务器收到请求。
  • 505(HTTP 版本不受支持):服务器不支持请求中所用的 HTTP 协议版本。

HashMap的实现原理

数组、链表、红黑树。

  • 在JDK8中,HashMap底层是采用“数组+链表+红黑树”来实现的。HashMap是基于哈希算法来确定元素的位置(槽)的,当我们向集合中存入数据时,它会计算传入的Key的哈希值,并利用哈希值取余来确定槽的位置。如果元素发生碰撞,也就是这个槽已经存在其他的元素了,则HashMap会通过链表将这些元素组织起来。如果碰撞进一步加剧,某个链表的长度达到了8,则HashMap会创建红黑树来代替这个链表,从而提高对这个槽中数据的查找的速度(O(logn))。如果在红黑树状态下,链表的长度小于等于6了,则会退化为数组+链表。

  • HashMap中,数组的默认初始容量为16,这个容量会以2的指数进行扩容。具体来说,当数组中的元素达到一定比例的时候HashMap就会扩容,这个比例叫做负载因子,默认为0.75。自动扩容机制,是为了保证HashMap初始时不必占据太大的内存,而在使用期间又可以实时保证有足够大的空间。采用2的指数(即两倍扩容)进行扩容,是为了利用位运算,提高扩容运算的效率。

  • HashMap是非线程安全的,所以在多线程环境下,各线程同时触发HashMap的改变时,都有可能会发生冲突。所以,在多线程环境下不建议使用HashMap,可以考虑使用Collections将HashMap转为线程安全的HashMap,更为推荐的方式则是使用ConcurrentHashMap。

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 数组
transient Node<K,V>[] table;

// 链表
static class Node<K,V> implements Map.Entry<K,V> {
...
Node<K,V> next;
}

// 红黑树
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
}


// 默认初始容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 16

// 最大容量(2^30)
static final int MAXIMUM_CAPACITY = 1 << 30;

// 默认负载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;

// 转为树的临界点
static final int TREEIFY_THRESHOLD = 8;

// 转链表的临界点
static final int UNTREEIFY_THRESHOLD = 6;

// 支持树结构的最小容量
static final int MIN_TREEIFY_CAPACITY = 64;

拓展

说说HashMap的put方法。

  1. 根据key通过哈希算法与与运算得出数组下标。

  2. 如果数组下标元素为空,则将key和value封装为Entry对象(JDK1.7是Entry对象,JDK1.8是Node对象)并放入该位置。

  3. 如果数组下标位置元素不为空,则要分情况

  • 如果是在JDK1.7,则首先会判断是否需要扩容,如果要扩容就进行扩容,如果不需要扩容就生成Entry对象,并使用头插法添加到当前链表中。

  • 如果是在JDK1.8中,则会先判断当前位置上的TreeNode类型,看是红黑树还是链表Node。

如果是红黑树TreeNode,则将key和value封装为一个红黑树节点并添加到红黑树中去,在这个过程中会判断红黑树中是否存在当前key,如果存在则更新value。

如果此位置上的Node对象是链表节点,则将key和value封装为一个Node并通过尾插法插入到链表的最后位置去,因为是尾插法,所以需要遍历链表,在遍历过程中会判断是否存在当前key,如果存在则更新其value,当遍历完链表后,将新的Node插入到链表中,插入到链表后,会看当前链表的节点个数,如果大于8,则会将链表转为红黑树

将key和value封装为Node插入到链表或红黑树后,在判断是否需要扩容,如果需要扩容,就结束put方法。

分代回收机制

前言

分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。

在Java程序运行的过程中,会产生大量的对象,其中有些对象是与业务信息相关,比如Http请求中的Session对象、线程、Socket连接,这类对象跟业务直接挂钩,因此生命周期比较长。但是还有一些对象,主要是程序运行过程中生成的临时变量,这些对象生命周期会比较短,比如:String对象,由于其不变类的特性,系统会产生大量的这些对象,有些对象甚至只用一次即可回收。

如果在不进行对象存活时间区分的情况下,每次垃圾回收都是对整个堆空间进行回收,花费时间相对会长,同时,因为每次回收都需要遍历所有存活对象,但实际上,对于生命周期长的对象而言,这种遍历是没有效果的,因为可能进行了很多次遍历,但是他们依旧存在。因此,分代垃圾回收采用分治的思想,进行代的划分,把不同生命周期的对象放在不同代上,不同代上采用最适合它的垃圾回收方式进行回收。

主菜

虚拟机中的共划分为三个代:年轻代(Young Generation)、年老点(Old Generation)和持久代(Permanent Generation)。而新生代中又分为幸存者区(Suevivor)和伊甸园区(Eden),而在幸存者区又划分出了to和from两个区域。

其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。年轻代和年老代的划分是对垃圾收集影响比较大的。

新生代:朝生夕灭的对象(例如:方法的局部变量等)。

老年代:存活得比较久,但还是要死的对象(例如:缓存对象、单例对象等)。

永久代:对象生成后几乎不灭的对象(例如:加载过的类信息)。

分代回收

  • 新生代回收(Minor GC/Young GC):指只是进行新生代的回收。

  • 老年代回收(Major GC/Old GC):指只是进行老年代的回收。目前只有 CMS 垃圾回收器会有这个单独的回收老年代的行为。(Major GC 定义是比较混乱,有说指是老年代,有的说是做整个堆的收集,这个需要你根据别人的场景来定,没有固定的说法)

  • 整堆回收(Full GC):收集整个 Java 堆和方法区(注意包含方法区)


本文作者: 墨水记忆
本文链接: https://tothefor.com/DragonOne/6255a055.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!