关于新生代和老年代怎么划分的问题,这块主要是一些配置,刚才我们也说了分析器这一块
分配了1M的内存大小,
我现在用的是JDK1.8了,它会生成一个文件,1.8分配太小了,他就启动不了,
因为我是准备分配5M的,虚拟机加载的时候也需要一定的内存
JAVA虚拟机提供了参数-Xss来指定线程的最大栈空间,整个参数也直接决定了函数可调用的最大深度.
有些时候我们写一些递归循环,然后虚线递归总会报一些错,超过栈的最大深度了,这块也是很常见的一个事,
-Xss 1m
-Xss 5m
调用的最大深度是多大
就是我调用了2万多次跑异常了,如果我把参数改一下,我改成4
这个深度就有差别了,这就是10万多了,调用的次数会不断的增加,根据你深度的配置
和堆一样,方法区也是一块所有线程共享的内存区域,它用于保存类的类信息,方法区可以保存多少信息,可以对其
进行配置,默认情况下是64M,-XX:MaxPermSize默认是64M,如果系统运行时生产大量的类,就需要设置一个相对
合适的方法区,避免出现永久区内存溢出的问题.
-XX:PermSize=64M, -XX:PermSize=64M
一般这个参数也可以配置到你的应用中
直接内存:在NIO的时候,这个配置无所谓,你也可以不配,如果你以后要用原生的NIO的话,那你可以用一用,
但是如果你要用Netty这些框架,-XX:MaxDirectMemorgySize这个你可以不用去关注,如果不设置默认的最大值
就是堆空间,其实不设置就完事了,这可能是比较找的一个参数了,他们的访问速度可能会更快一些,直接就访问
原生的堆空间,跳过了JAVA的堆,这些了解即可
咱们工作中很少有这些东西,想想栈的配置什么时候会用到,我现在要执行一个递归的操作,什么时候要去调节
一下,有没有想过这个问题,你在写一些递归的应用的时候,比如说你有一个管理机构,或者有一个树形列表很大的
tree,比如你有一个很大的树形,树很深,这个时候你就可以用这个方法了,你可能在程序写的时候,因为树可能是
好多层的,几千层几万层,这个时候你想在JAVA里面写程序的话,递归调多少次是有限制的,不能无限制的递归,
一般的来讲,你去遍历一棵树的时候,最起码有一个阀值判断,最终如果没有叶子节点的时候,我就 把这个递归给他
停掉了,但是如果某一种情况下,我确实有这么长的树,一次次的去遍历一下,那这个就没办法了,最大的深度的调用
一般是做一些算法运算的时候,你们去写JAVA代码,你们涉及到这些运算不多,都是一些应用级别的,你真正写算法
的时候,考量调一下这个参数
虚拟机的工作模式
早在JAVA1.6的时候,其实有两个版本的模式,一个版本是client模式,还有一个版本是server模式,我们的JAVA
虚拟机早起是有两个模式的,java -version 命令就可以看到启动的JAVA虚拟机不说的话就是Server端的模式
早起你在运行程序的时候是有-server 和 -client这两个参数的,client相对于server的模式启动会比较快,
如果你不追求系统的长时间启用,仅仅是做一些性能上的测试的话,用client的就可以了,server模式启动的比较慢
原因是对其进行复杂的系统信息的收集和使用复杂的的算法对程序进行优化,所以启动的时候比较耗时,一般我们
的生产环境都会使用server模式,长期运行其性能要远远的高于client模式.
当然JDK1.7之后就没有client模式了,只有server模式了,所以你现在运行的都是server模式,除非你用JDK1.5,
JDK1.6才可能会有,JVM参数详细的说明,可能我讲的没有特别的详细,但大概也都说了,里边还有好多不常用的,
关闭GC的,但是你会关闭吗,如果关闭你还要手动去做,还有好多参数你可以去浏览一下,JVM参数说实话还是比较多
的,你在真正工作中,实际遇到问题了,再去做这种调优,如果你不关心,其实你学不学无所谓,这块了解就可以了,
如果问你怎么去调优,你根据不同的生产环境,然后去做一些调优才有意义,我也是发到群里
谈到垃圾收集(Garbage Collection),简称GC,首先你要澄清这个垃圾是什么概念,线程池,连接池大家有没有
一个概念呢,池子就是一个线程池,咱们的JDBC是一个线程池,比如有五个线程池,如果你JAVA的代码用到了连接了,
我就给你放在使用,用完了之后我再给他归还,池子一般初始化大小有多少个,一般的都用过druid的,阿里巴巴的,
连接超时,断连这一块,我给你看一段代码吧,这是我最近遇到的一个问题,这个问题可能我没太看仔细,我们打开
一段代码,我最近在做rabitmq的时候,在做MQ的时候,发生了一个小问题,就和activemq差不多,rabitmq是短连接
短连接是什么概念呢,我去发一条数据的时候,他去get一条connection,如果已经存在就直接去取,如果不存在
就直接getConnection(),就再实例化一个,如果我的链接为空,或者connection不可用
你看我这里写的是或者的关系,如果你的这个连接是空的,或者这个连接是不可用的,那我就让connectionFactory
去实例化一个connection
es是我们刚才的ExecutorService,然后我就写好是10个,我们配置了一堆参数了,配置在哪个路由,内容是什么,
是不是持久化,然后把它放到什么样的队列,调用发送消息的方法,发送Rabbitmq的集群,总之就是一个MQ的集群,
当我们finally的时候,finally和其他的写的还不一样,不是关闭连接,无论是在activemq还是rabbitmq的时候,
我可以通过一个connection,去创建多个session,很多公司其实底层并没有研究的这么细,我真正在用的时候会
发现一些小问题的,其实消费者倒无所谓了,我之前给你看的实例的时候
关闭connection,session也是通过connection实例化出来的,也会跟着把session也关闭了,就相当于我在
调一个connection.close(),先检查一下他有多少个connection,然后在关闭自己的connection,如果每次想
发一些数据的时候,你发送批量数据还好说,如果我们的producer再生产者这一端,一般都是采用短连接的形式,
发一条数据关一下连接,这个时候连接不断的打开和关闭,非常浪费性能,这个我在真正的实验也测出一些问题,
性能并不是那么高,无论什么样的公司,无论从什么样的角度去考虑问题,当然这是一个细节的小问题,不是那么大
的事情,如果那么耗资源的一个操作的话,我个人觉得是不可取的,当然API说是可以创建多个Session,所以这一块
这个节点断线了,因为我采用集群的模式,连接这一块我写死了,虽然说写死了,这个连接我可以连三个集群,但是
我确实是连接一个host,这个host就是我的VIP,就是虚拟出来的,这个IP会负载均衡后面三个MQ的服务器,它使用
HVProxy去做的,咱们就简单的说一下,如果说三个节点,开始我的程序是连接VIP
我这里可能组成一个集群了,有多个点,比如由三个MQ组成一个集群,然后现在我想做负载均衡的事情,我现在做的
负载均衡是什么的负载均衡,是MQ端的负载均衡,也就是我的应用程序Producer这一块,去发数据的时候,这样就
降低与MQ的压力了,把连接请求打散,我又多个producer就可以并行的往里传,并不需要任何的等,这个就需要在
中间加一个东西,说白了就是一个负载均衡的概念,其实负载均衡和反向代理是两个概念,其实真正意义上的负载
均衡LVS和H5,而Nginx和hyproxy这种都是貌似负载均衡的反向代理服务器,如果你在这里用LVS的话,生产者
连接的肯定是0这个节点,LVS就会改底层的数据包,就会改底层的IP,就相当于给你负载均衡,然后做到了进来的IP
地址是0,然后往外推数据的时候就是1,2,3,然后返回数据的时候不是LVS帮你返回的,而是1,2,3直接帮你返回了
这才叫真正意义上的负载均衡,就是TCP/IP级别的负载均衡,而你所谓的反向代理,比如说Nginx,比如说
HAProxy像这种东西,进来的确实是0,然后在里面配置1,2,3,让这三个哥们去做负载均衡,我去把请求给你,
你呢给我,然后我再去给他,我就相当于一个proxy,所以这个才叫做反向代理,你把请求给我,我把请求通过我的
配置,Nginx不是配置有2吗,负载均衡给2,服务器2处理完成之后就给Nginx,Nginx然后再给producer,这种实际上
就是一种代理的角色,其实服务器的压力完全就在代理服务器上,如果说是做LVS的话,Nginx其实代理其实是最弱的
因为Nginx其实是WEB端的服务器,它并不是专门做负载均衡的,它做负载均衡其实就是upstream,加个大括号{},
配置三个IP地址,它是通过upstream模块去实现负载均衡的,Nginx,HAProxy也是通过一个配置项去实现负载均衡
的,处理完之后还是会返给我,我的服务器再给前端的client端,它是这样一个模式,就是关于负载均衡和反向代理
的概念,这个你要清楚,面试的时候别人会问你,说负载均衡和反向代理,很多人在学习的时候
我这里三个服务器组成一个集群了cluster,然后我不管你用不用反向代理,其实你真正的Producer生产者,
你想给我去发数据,你只能去连集群中的一个节点,这个是毋庸置疑的,跟集群中的某个节点做通信,无论是mysql的
主从,现在mysql做集群方案多了去了,包括ORACLE的集群方案,最终Connection连接的时候只连接一个节点,
无论什么集群,不可能一个连接走三份,那这些怎么玩,所以说要理解这个事情,那么假设说,在进行连接的时候,
我这个集群就直接断掉了,断掉了下面的代码可能就不可用了,因为这个连接已经挂掉了,跑异常的话肯定会走
catch,走catch就会取走close,我去把之前的连接去给释放了,connection.close()之后connection是个
什么状态呢,我调close是一个什么样的状态,第一点它不可能马上响应你的close,它可能会调用close方法,
它调用close会占用程序的资源,释放之后需用使用GC是回收connection之前所拥有的资源,调用这个方法就是
想把这个对象清空,如果你这么去写的话会有一个问题,它并不是马上把这个connection滞空的,connection为空
connection不可用,是不是在打开连接的状态,这样我再去创建一个连接,等待这边慢慢的关闭了,下次GC来的时候
去把GC释放了,这是一个不错的方案,或者你就暴力一点,close之后就手工的去做这种事情,
手工的去做也不知道是不是马上就为空了,因为GC的这个事情你不敢保证,GC并不是你代码写成null,下一秒就能把
你制成空,所以这种可能还是保证的不那么彻底,就算你写成空,也必须GC才能滞空,很多时候会有这么一个问题,
我这个集群万一中途挂了,那会是一个怎样的场景
以后无论你是写连接池的时候,你一定要注意这个问题,然后就更进一步的确认了GC并不是做什么事,
rabbitmq的启动速度超级快,
RabbitMQ没有监听的概念,都是短连接,receive的状态一直在变,每次发一条就断开一个连接再连,在finally的
时候都是connection.close(),连接都是关闭了,因为在接收这段性能都是很高的
JVM去GC的,而不是你自己去做的.