(四)Redis 数据类型之List实现数据结构
栈:先进后出
lpush + lpop
左侧入栈,左侧出站
队列:先进先出
lpush + rpop
左侧入栈,右侧出栈,队列思想
有限集合
lpush + ltrim
消息队列
lpush + brpop
左侧不断入栈,通过brpop不断消费List中数据
lpush + lpop
左侧入栈,左侧出站
lpush + rpop
左侧入栈,右侧出栈,队列思想
lpush + ltrim
lpush + brpop
左侧不断入栈,通过brpop不断消费List中数据
Spark的体系结构不同于Hadoop的MapReduce和HDFS,Spark主要包括Spark Core和在Spark Core基础之上建立的应用框架Spark SQL、Spark Streaming、MLlib和GraphX。
Core库中主要包括上下文(Spark Context)、抽象数据集(RDD)、调度器(Scheduler)、洗牌(shuffle)和序列化器(Serializer)等。Spark系统中的计算、IO、调度和shuffle等系统基本功能都在其中。在Core库之上就根据业务需求分为用于交互式查询的SQL、实时流处理Streaming、机器学习Mllib和图计算GraphX四大框架,除此外还有一些其他实验性项目如Tachyon、BlinkDB和Tungsten等。这些项目共同组成Spark体系结构,当然Hadoop中的存储系统HDFS迄今仍是不可被替代,一直被各分布式系统所使用,它也是Spark主要应用的持久化存储系统。
Netty 是一个 基于 NIO 的 client-server(客户端服务器)框架,使用它可以快速简单地开发网络应用程序。
它极大地简化并优化了 TCP 和 UDP 套接字服务器等网络编程,并且性能以及安全性等很多方面甚至都要更好。
支持多种协议 如 FTP,SMTP,HTTP 以及各种二进制和基于文本的传统协议。
在讨论负载均衡之前,我想先解释一下这3个概念。
负载均衡
集群容错
服务路由
这3个概念容易混淆。他们都描述了怎么从多个 Provider 中选择一个来进行调用。那他们到底有什么区别呢?下面我来举一个简单的例子,把这几个概念阐述清楚吧。
有一个Dubbo的用户服务,在北京部署了10个,在上海部署了20个。一个杭州的服务消费方发起了一次调用,然后发生了以下的事情:
可以看到Dubbo中的路由,负载均衡和集群容错发生在一次RPC调用的不同阶段。最先是路由,然后是负载均衡,最后是集群容错。 本文档只讨论负载均衡,路由和集群容错在其他的文档中进行说明。
顺序上:
- 服务路由
- 负载均衡
- 集群容错
一起七个 3 + 3 + 1
在收集器使用的时候都是:新老搭配的。
新生代收集器:
老年代收集器:
新老通吃:
G1: Garbadge First
HotSpot运行在client模式下的默认新生代收集器。只用一个CPU/一个收集器线程去完成GC。
“Stop The World” -XX:+UseSerialGC
其本质是Serial的多线程版本,目的是缩短垃圾收集的时间,也就是占用工作线程时间的问题。单CPU甚至不如Serial,但是在多核CPU配合多线程效果明显。多核CPU + 超线程技术。
若VM启用老年代使用CMS(concurrent mark sweep)时候,
参数: -XX:+UseConcMarkSweepGC
新生代次收集器默认为ParNew。
多核CPU时候一般配置为CPU核数。-XX:ParallelGCThreads=
并行多线程,复制算法。关注点与ParNew不同,关注的是系统吞吐量。
系统吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾回收时间)
停顿时间越短就越适用于用户交互的程序。良好的响应速度提升用户体验。
而高吞吐则适用于后台计算而不需要太多交互的任务,可以最高效率的利用CPU时间,
尽快的完成程序的运算任务。
可以与任意的新生代收集器配置使用
只能配合Parallel Scavenge收集器一起使用。
Concurrent Mark Sweep,可以和用户线程同时工作。是一款并发收集器。
虽然有理论上表现更好的G1收集器,主流的仍然是CMS收集器。
能用于server模式下的JVM优化。能结合新生代的Serial 和 ParNew一起使用。
Garbage-First。可以运用在新生代和老年代的直接分区收集。面向服务端的垃圾收集器。 –XX:+UseG1GC
将Java堆划分为多个相等的独立区域Region,保留新生代和老年代的概念。但是不再是物理隔离。
优化方式:
1.选择JVM版本
2.对于堆区大小的分配
3.垃圾回收的方式
jps
查询运行于jvm的线程jps -l
jstat
查看HotSpot VM运行的信息
jstat -gc pid 毫秒数 次数
From Created
From Used
To Created
To Used
Eden Created
Eden Used
Old Created
Old Used
Permanent Created
Permanent Used
Young GC 次收集器收集次数
Young GC Time 次收集器时间
Full GC 全收集次数
Full GC Time 全收集时间
GC Time 收集总时间
S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
jvisualvm
可视化工具
jvisualvm
Scavenge(次收集) 和 Full GC(全收集)区别
新生代GC (Scavenge GC): 发生在新生代的GC,因为新生代的Java对象大多是朝生夕死,所以Scavenge GC非常频繁。
回收速度快,当Eden内存空间不足时,会触发Scavenge GC。
一般情况下,当新对象生成时,并且在Eden中申请空间失败,就会触发Scavenge GC,对Eden区进行GC,清除非存活对象,
将尚存活的对象移动至Survivor区,然后整理两个Survivor区,这种方式的GC是对年轻代的Eden区进行,不会影响老年代。
老年代GC (Full GC/Major GC): Full GC是指发生在老年代的GC。出现了Full GC一般至少会伴随一次Minor GC。
老年代的对象大多是Minor GC过程中从新生代进入老年代。比如分配担保失败。Full GC的速度一般会比Minor GC慢十倍以上。
当老年代内存不足时或者显式调用System.gc()时候,会触发Full GC。
次收集:
当年轻代堆空间紧张会被触发,相对于全收集,收集间隔较短。
全收集:
当老年代或者持久代空间满了时候会触发全收集操作。可以使用System.gc()显式调用。
全收集一般会根据堆大小,需要的时间较长。不过全收集时间超过3s-5s,那就太长了。
新生代收集器:
Serial
ParNew
Parallel scavenge
老年代收集器:
CMS(Concurrent Mark Sweep)
Serial Old
Parallel Old
新老通吃:
G1: Garbadge First
三个区的默认存放空间是Eden : From : To = 8: 1: 1
目的是因为HotSpot采用复制算法来回收新生代,设置这个比例是为了充分利用空间。
新生成的对象优先存放在新生代中,在新生代中,常规应用采取一次垃圾收集可以回收70%-90%。
新生成的对象在Eden区中分配(大对象除外,直接老年代)。
当Eden没有足够空间,虚拟机发起一次Minor GC。(只会收集一次Eden区)
GC开始时,对象只会存在于Eden区和From Suvivor区,To Survivor为空。用于复制
GC进行时,Eden区中所有存活的对象会被复制到To Survivor(连续存储),
而在From Survivor区中仍存活的对象,会根据年龄决定去向。
阈值为15,每熬过一次GC,年龄就加1,GC分代年龄值存在对象的header中,
大于15,移至老年代中,没有达到15的复制到To Survivor。
之后,From和To交换角色。From被清空,变成下一次GC的To区, 而To区成为下一次GC的From区。
无论如何,保证To区域在下一轮GC中为空的。
note: 当TO区域用于复制From中的对象时,出现空间不足的时候,需要依赖老年代分配内存进行存放。
在新生代中经历了多次的GC后存活下来的对象会进入老年代。
老年代的对象生命周期较长,存活率比较高,在老年代中进行GC的频率较低,效率也较低。
永久代中存放类信息,常量,静态变量,即时编译器编译后的代码等数据。
对此区域,Java虚拟机规范指出可以不进行垃圾收集。一般而言不会进行垃圾回收。
古老的算法。此对象有一个引用,增加一个计数,删除一个引用减少一个引用。垃圾回收时,只用收集为0的对象。
致命问题是无法解决循环引用的问题。
eg:survivor from to
此算法将内存空间分为两份。每次使用其中一个区域。(占用空间)
垃圾回收时,遍历当前使用的区域,把正在使用中的对象复制到另一个区域中。
执行分为两阶段。
第一阶段:从对象根节点开始标记所有被引用的对象。
第二阶段:遍历整个堆,把未标记的对象清除,此算法需要暂停整个应用(Stop the world)。
同时会产生内存碎片。
缺点:
1)未使用的内存空间不连续,浪费。
2)暂停整个应用。
执行分为两阶段。
第一阶段:从对象根节点开始标记所有被引用的对象。
第二阶段:遍历整个堆,把未标记的对象清除,同时把存活的对象压缩到一块,顺序排放,避免碎片问题。
避免了复制算法的空间问题。
从文件系统中加载class文件,加载类信息。加载的信息存放于一个叫做方法区的内存空间。
方法区中包括: 类信息,常量池(字符串字面量和数字常量)
主要内存空间。存放对象的实例。线程共享
NIO允许Java程序直接访问直接内存。
不受JVM内存 -Xmx 最大堆大小限制。不属于堆内存。直接内存速率高于堆内存。
读写频繁的场合可能会用到直接内存,与系统内存挂钩。
回收java堆,方法区,直接内存都可以回收。java所有的对象的释放都是隐式的。
垃圾回收系统后台默默运行,标示和释放对象,实现全自动化管理。
每一个虚拟机线程都有一个私有的栈,栈在线程创建的时候被创建。栈保存着帧信息。
栈中保存方法中的局部变量,方法参数,和程序的调用,返回相关。
本地方法栈和虚拟机栈类似,虚拟机栈用于方法的调用。本地方法栈则用于本地方法(Native API)的调用。
Java虚拟机的重要拓展之一,不同的操作系统本地方法的API也不一样。
线程私有,字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,
分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完。