1.数据库创建索引有什么优点和缺点?
优点:
第一,可以保证数据库表中每一行数据的唯一性。
第二,可以大大加快数据的检索速度。
第三,可以加速表和表之间的连接。
第四,可以显著减少查询中分组和排序的时间。
第五,通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
缺点:
第一,创建索引和维护索引要耗费时间。
第二,索引需要占物理空间,每一个索引还要占一定的物理空间。
2.连接池的作用,优点?
优点:
一、资源重用(避免了频繁创建、释放连接引起的大量性能开销)
[if !supportLists]二、[endif]更快的系统响应速度(直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销)
[if !supportLists]三、[endif]新的资源分配手段(某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源)
[if !supportLists]四、[endif]统一的连接管理,避免数据库连接泄漏(可根据预先的连接占用超时设定,强制收回被占用连接)
缺点:资源的浪费。
3.java多线程?
一、Java多线程实现的方式有四种
1.继承Thread类,重写run方法
2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target
3.通过Callable和FutureTask创建线程
4.通过线程池创建线程
原文:https://blog.csdn.net/u011480603/article/details/75332435
二、java线程池
任务型线程池 -- newScheduledThreadPool --创建一个定长线程池,支持定时及周期性任务执行。
定长型线程池--newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等 待。
缓存型线程池--newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空 闲线程,若无可回收,则新建线程。
单线程线程池--newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任 务,保证所有任务按照指定顺序(FIFO, LIFO,优先级)执行。
原文:https://www.cnblogs.com/zhaoyan001/p/7049627.html
[if !supportLists]三、[endif]Java线程池ThreadPoolExecutor的4种拒绝策略
原文:http://www.cnblogs.com/cblogs/p/9444557.html
4.SpringBean的生命周期?
Bean在Spring Bean工厂中的生命周期:
第1步、实例化
第2步、设置属性值
第3步、调用BeanNameAware的setBeanName()方法
第4步、调用BeanFactoryAware的setBeanFactory()方法
第5步、调用InitializingBean的afterPropertiesSet()方法
第6步、调用定制的初始化方法
第7步、Bean可以使用了
第8步、容器关闭
第9步、调用DisposableBean的destory()方法
第10步、调用定制的销毁方法
5.hashMap和hashTable的区别?
[if !supportLists]1) [endif]hashMap的key可以为null ----- hashTable 的key不能为null.
[if !supportLists]2) [endif]hashMap继承自AbstractMap类 -- hashTable 继承自Dictionary类.(迪克什勒瑞)
[if !supportLists]3) [endif]hashMap是线程不安全的 --- hashTable是线程安全的(部分方法上用synchronize )
[if !supportLists]4) [endif]hashMap数组默认大小为16 -- hashTable数组的默认大小是11
5) concurrentMap线程安全 速度慢 分段锁 不是整体锁
concurrentMap适合用于并发编程,其实就是在要求线程安全的时候用它
每个segment的读写是高度自治的,segment之间互不影响
补充:到了jdk1.8,当同一个hash值的节点数不小于8时,不再采用单链表形式存储,而是采用红黑树
6. [endif]输入一个网址访问具体流程是怎么样的?
(1)浏览器本身是一个客户端,当你输入URL的时候,首先浏览器会去请求DNS服务器,通过DNS获取相应的域名对应的IP
(2)然后通过IP地址找到IP对应的服务器后,要求建立TCP连接
(3)浏览器发送完HTTP Request(请求)包后,服务器接收到请求包之后才开始处理请求包
(4)在服务器收到请求之后,服务器调用自身服务,返回HTTP Response(响应)包
(5)客户端收到来自服务器的响应后开始渲染这个Response包里的主体(body),等收到全部的内容随后断开与该服务器之间的TCP连接。
7. [endif]线程和进程?
一个程序最少需要一个进程,而一个进程最少需要一个线程。
关系是线程–>进程–>程序的大致组成结构。
所以线程是程序执行流的最小单位,而进程是系统进行资源分配和调度的一个独立单位。
8. [endif]Synchronized与Lock的区别与应用场景
1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
6. [endif]各个数组的区别?
7. [endif]SpimgMVC工作原理?
第1步:浏览器发送指定的请求都会交给DispatcherServlet(前置控制器),他会委托其他模块进行真正的业务和数据处理
第2步:DispatcherServlet会查找到HandleMapping,根据浏览器的请求找到对应的Controller,并将请求交给目标Controller
第3步:目标Controller处理完业务后,返回一个ModelAndView给DispatcherServlet
第4步:DispatcherServlet通过ViewResolver视图解析器找到对应的视图对象View
第5步:视图对象View负责渲染,并返回到浏览器
原文:https://blog.csdn.net/u010452388/article/details/80930609
[if !supportLists]8. [endif]StringBuffer 和 StringBuild 的区别?
执行效率:stringbuild>stringbuff>string
[if !supportLists]1) [endif]String类是不可变类,任何对String的改变都会引发新的String对象的生成;
[if !supportLists]2) [endif]StringBuffer是可变类,任何对它所指代的字符串的改变都不会产生新的对象,线程安全的。
[if !supportLists]3) [endif]StringBuilder是可变类,线性不安全的,不支持并发操作,不适合多线程中使用,但其在 单线程中的性能比StringBuffer高。
[if !supportLists]9. [endif]mybatis中的#{}和${}有什么区别?
#{}:是以占位符的方式.
${}:拼接的方式.有sql注入的风险.
例如:SELECT * FROM表名 WHERE passWord= '1' OR 1=1
[if !supportLists]10. [endif]项目中哪些地方用到了多线程?
场景一:
一个业务逻辑有很多次的循环,每次循环之间没有影响,比如验证1万条url路径是否存在,正常情况要循环1万次,逐个去验证每一条URL,这样效率会很低,假设验证一条需要1分钟,总共就需要1万分钟,有点恐怖。这时可以用多线程,将1万条URL分成50等份,开50个线程,没个线程只需验证200条,这样所有的线程执行完是远小于1万分钟的。
场景二:
需要知道一个任务的执行进度,比如我们常看到的进度条,实现方式可以是在任务中加入一个整型属性变量(这样不同方法可以共享),任务执行一定程度就给变量值加1,另外开一个线程按时间间隔不断去访问这个变量,并反馈给用户。
6. 怎么样让list遍历出来的结果是有顺序的,谈谈你知道的排序方法?
1) Collections.sort(list);
2) Pojo类实现Comparable<T>接口,重写compareTo(T)方法即可
3)Collections.sort(list, new Comparator() {},再重写compare方法
**JAVA常用4种排序方法:快速排序法、冒泡法、选择排序法、插入排序法、希尔排序(甚至还有基数排序、鸡尾酒排序、桶排序、鸽巢排序、归并排序等)
12,什么叫线程安全的?
如果你有一个方法,存在同时多个线程同时访问它,如果结果与单线程访问的预期结果一样,那么就说线程安全,如果结果不一样就说线程不安全
举例比如一个ArrayList类,在添加一个元素的时候,它可能会有两步来完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
在单线程运行的情况下,如果Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1; 而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。 那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。
13.数据库的三大范式?
1NF: 字段是原子性的, 不可分 ;
2NF: 有主键,非主键字段依赖主键 。确保一个表只说明一个事物
3NF: 非主键字段不能相互依赖 。 每列都与主键有直接关系,不存在传递的依赖
14. 事务的4种隔离级别
1,读未提交,就是一个事务可以读取另一个未提交事务的数据。
2,读已提交,就是一个事务要等另一个事务提交后才能读取数据。Orecal
3,重复读,就是在开始读取数据(事务开启)时,不再允许修改操作 Mysql
4,Serializable是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、 不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
15. 数据库中事务的四大特性
[if !supportLists]1) 原子性--转账:要么同时成功,要么同时失败
[if !supportLists]2) 一致性--转账:执行前后数据都是完整的
[if !supportLists]3) 隔离性--转账:事务和事务之间是完全隔离的
[if !supportLists]4) 持久性--转账:事务完成后对数据的改变会永久的存储起来
如果不考虑事务的隔离性,会发生的几种问题:
脏读,不可重复读,虚读(幻读)
16. 说说Maven的生命周期?
通过pom.xml文件Maven可以管理项目的整个生命周期,包括清除、编译,测试,报告、打包war、部署等等。
17. Linux常用命令?
举例:在Liunx上安装tomcat
[if !supportLists]1. [endif]在SecureCRT中远程连接Linux,输入alt+p进入sftp界面
[if !supportLists]2. [endif]在sftp界面输入put d:/setup/tomcat7.tar.gz,回车
3. [endif]回到操作界面,输入LL命令查看目录结构中是否含有tomcat7.tar.gz
4. [endif]然后输入tar -zxvf tomcat7.tar.gz命令,把文件压缩包解压
5. [endif]输入cd tomcat7进入文件夹目录,再输入LL查看目录
6. [endif]如果想创建文件夹,输入mkdir文件夹名命令,即可
7. [endif]如果想修改文件名,输入mv原文件名新文件名命令,即可
8. [endif]如果想打开文件,输入vim文件名命令,点击i进入编辑模式,ESC退出编辑,输入:wq,保 存成功
6. 谈谈cookie和session区别?
cookies:浏览器自己的缓存,主要存储用户的设置,浏览记录等。(cookies生存期限就到关闭浏览器为止)
Session: 只要客户机访问,程序就会为这个客户新增一个session。session里主要保存的是用户的登录信息,操作信息等。这个session在用户访问结束后会被自动消失(默认30分钟)。
1) [endif]cookie数据存放在客户的浏览器上,session数据放在服务器上。
2) [endif]cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
3) [endif]考虑到安全应当使用session。
4) [endif]session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
5) [endif]考虑到减轻服务器性能方面,应当使用COOKIE。
6) [endif]单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie
Spring的IOC和AOP原理?
在spring ioc中有三种依赖注入
a、接口注入;
b、setter方法注入;
c、构造方法注入;
d、注解方法注入;
IOC : 控制反转,所有的对象都交由spring来管理,当需要使用时,从容器中获取, 底层使用反射技术; 由spring来负责控制对象的生命周期和对象间的关系, 所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
静态代理模式:静态代理说白了就是在程序运行前就已经存在代理类的字节码文件,代理类和原始类的关系在运行前就已经确定。(增加了代码的维护成本)
动态代理模式:动态代理类的源码是在程序运行期间通过JVM反射等机制动态生成,代理类和委托类的关系是运行时才确定的。
AOP:面向切面编程思想,减少一些硬编码,主要用来事务管理器/日志/权限/错误处理;底层使用动态代理,bean标签中proxy-target-class默认为false,如果被代理对象实现了接口,使用jdk动态代理,反之如果被代理类没有实现接口,就使用cglib动态代理;如果我们将proxy-target-class设置为true就只会调用cglib实现动态代理
AOP参考:https://blog.csdn.net/yinni11/article/details/80217241
Jdk的proxy代理针对有实现接口的类,创建代理类实际上就是实现被代理类所有的接口;
Cglib主要是针对没有实现接口的类,创建的代理类实际上就是继承被代理类;
spring常用注解:@Controller @Trancational @autowired @Resource @Value
Spring的事务管理机制:就是通过动态代理加载被事务管理的BEAN,并根据配置在invoke方法中对当前调用的方法名进行判定,并在method.invoke方法执行前后进行事务管理
6. 动态代理?
1) [endif]什么是代理模式?
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。
2) [endif]代理模式有什么好处?
在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
注:JDK动态代理要比cglib代理执行速度快,但性能不如cglib好。所以在选择用哪种代理还是要看具体情况
6. [endif]activeMQ使用,应用场景?
消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来
进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。对于消息中间件,常见的角色大致也就有Producer(生产者)、Consumer(消 费者)
1) 异步处理:将不是必须的业务逻辑,异步处理。 -- 短信微服务
2) 应用解耦:实现订单系统与库存系统的应用解耦。-- 点对面,运营商后台和网页生成服务
3) 流量削峰:一般在秒杀或团抢活动中使用广泛。
a、让MQ可以控制活动的人数。
b、让并发到MQ中,可以缓解短时间内高流量压垮应用。
1) 日志处理: kafka,解决大量日志传输的问题。
2) 消息通讯:比如实现点对点消息队列,或者聊天室等。
电商系统应用:
(1)应用将主干逻辑处理完成后,写入消息队列。消息发送是否成功可以开启消息的确认 模式。(消息队列返回消息接收成功状态后,应用再返回,这样保障消息的完整性)
(2)扩展流程(发短信,配送处理)订阅队列消息。采用推或拉的方式获取消息并处理。
(3)消息将应用解耦的同时,带来了数据一致性问题,可以采用最终一致性方式解决。比 如主数据写入数据库,扩展应用根据消息队列,并结合数据库方式实现基于消息队列
的后续处理。
6. RabbitMQ常用的交换器类型有
1.fanout广播
发送到该交换器的消息将路由到所有与该交换器绑定的队列中
2..direct直投
将消息路由到BindingKey和RoutingKey完全匹配的队列中
3.headers比对headers属性
不依赖路由键的匹配来路由消息,而是根据发送的消息内容中的headers属性(键值对) 进行匹配,在绑定队列和交换器时也指定一组键值对,两者匹配时,即路由
headers类型性能会很差,也不实用,很少用到
4.topic模式匹配
将消息路由到BindingKey和RoutingKey相匹配的队列中,可以使用模式匹配规则
6. redis集群分布式?(6台)
客户端与redis节点直连,不需要中间 proxy 层.客户端不需要连接集群所有节点连接 集群中任何一个可用节点即可。
所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带
宽.
容错机制-投票
Redis集群中一般都有主节点和从节点,主副节点之间只是复制关系,当半数以上的节点与该节点通信失败,则认为该节点故障,自动触发故障转移操作,故障节点对应的从节点自动升级为主节点.
分布存储机制-槽
Redis集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
6. [endif]MyCat分片-海量数据存储解决方案
1) 垂直切分--不同的表存储到不同的数据库
2) 水平切分--把一个表的数据拆分到多台数据库里
3) 按主键范围分片
4) 按一致性哈希分片
mysql的主从复制
要想实现读写分离必然要说到mysql的主从复制,因为你一旦做了读写分离必然要面临的第一个问题就是数据库数据怎么同步我们往我们的写库里面添加了数据但是读库不知道,这肯定是不行的所以我们要配置mysql的主从复制,一般来说我们把写库配置成主库读库配置成从库,我们往主库里面添加了数据之后,我们可以通过主库的日志(也可以理解为配置文件)方式来把你添加的数据同步到从库中去这个时候就能够保证数据同步,但是这个过程是有时间差的
如何实现读写分离
[if !supportLists]1) [endif]我们可以配置多个数据源通过程序的手动控制来控制读写
例如:我们可以使用spring的aop来实现,在进入service之前,我可以加一个切面来判断这个操作是读还是写,例如进根据方法名,一般query,find,get开头的都是查询,走读库,其他的走写库
[if !supportLists]2) [endif]我们可以引入中间键(有点像activemq)中间键可以自动的帮我们实现
6. Tomcat和nginx的区别?
Nginx优点:负载均衡、反向代理、处理静态文件优势。nginx处理静态请求的速度高于apache;
Tomcat优点:动态解析容器,处理动态请求,是编译JSP\Servlet的容器,Nginx有动态分离机制,静态请求直接就可以通过Nginx处理,动态请求才转发请求到后台交由Tomcat进行处理。
6. @Resource与@Autowired注解的区别
1、@Autowired(是Spring的注解。)是通过 byType 的方式去注入的, 使用该注解,要求接 口只能有一个实现类。
2、@Resource(是J2EE的注解)可以通过 byName 和 byType的方式注入, 默认先按 byName的方式进行匹配,如果匹配不到,再按 byType的方式进行匹配。
3、@Qualifier注解可以按名称注入, 但是注意是 类名。
6. 悲观锁和乐观锁
定义:
悲观锁(Pessimistic Lock):
每次获取数据的时候,都会担心数据被修改,所以每次获取数据的时候都会进行加锁,确保在自己使用的过程中数据不会被别人修改,使用完成后进行数据解锁。由于数据进行加锁,期间对该数据进行读写的其他线程都会进行等待。
乐观锁(Optimistic Lock):
每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁,但是在更新数据的时候需要判断该数据是否被别人修改过。如果数据被其他线程修改,则不进行数据更新,如果数据没有被其他线程修改,则进行数据更新。由于数据没有进行加锁,期间该数据可以被其他线程进行读写操作。
适用场景:
悲观锁:比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。
乐观锁:比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。
6. [endif]解决分布式事务控制
1) [endif]什么是本地事务?-- (@Transactional)
地事务就是用关系数据库来控制事务,关系数据库通常都具有ACID特性,传统的单体应用通常会将数据全部存储一个数据库中,会借助关系数据库来完成事务控制.
[if !supportLists]2) [endif]什么是分布式事务?
分布式系统中一次操作由多个系统协同完成,这种一次事务操作涉及多个系统通过网络协同完成的过程称为分布式事务。这里强调的是多个系统通过网络协同完成一个事务的过程,并不强调多个系统访问了不同的数据库,即使多个系统访问的是同一个数据库也是分布式事务.
[if !supportLists]3) [endif]解决方案
两阶段提交协议(2PC)
事务补偿(TCC)
消息队列实现最终一致
执行流程:
1、[endif]支付成功后,订单服务向本地数据库更新订单状态并向消息表写入”添加选课消息”, (通过本地数据库保证订单状态和添加选课消息的事务)。
2、[endif]定时任务扫描消息表,取出”添加选课任务”,并发向MQ。
3、[endif]学习服务接收到MQ中的”添加选课任务”,先查询本地历史任务表是否存在,存在则说 明已经添加过,否则向本地数据库添加选课任务。
(通过本地数据库保证订单状态和添加选课消息的事务)
[if !supportLists]4、[endif]学习服务接收到添加选课的消息,通过查询消息表判断如果已经添加选课也向MQ发 送”完成添加选课任务消息”,否则就添加选课,完成后也发送MQ”完成添加选课任务 的消息”。
[if !supportLists]5、[endif]订单服务接收到MQ中的消息后删除订单数据库中消息表的”添加选课消息”,为保证 后期对账,将消息表的消息先移到历史表再删除消息,表示此消息已完成。
6. [endif]SpringBoot优点总结
SpringBoot核心功能
1、独立运行Spring项目
Spring boot可以以jar包形式独立运行,运行一个Spring Boot项目只需要通过java -jar xx.jar来运行。
2、内嵌servlet容器
Spring Boot可以选择内嵌Tomcat、jetty或者Undertow,这样我们无须以war包形式部署项目。
3、提供starter简化Maven配置
spring提供了一系列的start pom来简化Maven的依赖加载,例如,当你使用了spring-boot-starter-web,会自动加入如图5-1所示的依赖包。
4、自动装配Spring
SpringBoot会根据在类路径中的jar包,类、为jar包里面的类自动配置Bean,这样会极大地减少我们要使用的配置。当然,SpringBoot只考虑大多数的开发场景,并不是所有的场景,若在实际开发中我们需要配置Bean,而SpringBoot灭有提供支持,则可以自定义自动配置。
5、准生产的应用监控
SpringBoot提供基于http ssh telnet对运行时的项目进行监控。
6、无代码生产和xml配置
SpringBoot不是借助与代码生成来实现的,而是通过条件注解来实现的,这是Spring4.x提供的新特性。
6. [endif]jdk1.8新特性
1、[endif]default关键字
在接口中,用default修饰的方法是不需要去实现的,直接在接口中定义好,在别的地方可以直接调用
2、[endif]Lambda表达式
lambda表达式的一个最直观的作用就是大大的简化了代码的开发
3、[endif]函数式接口
jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。
4、[endif]方法与构造函数引用
5、[endif]局部变量限制
6、[endif]Date Api更新
7、[endif]Optional类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
8、[endif]流
6. [endif]JVM的内存结构?
Jvm:程序计数器+虚拟机栈 + 本地方法栈 + 堆 + 方法区
程序计数器:
程序计数器(Program Counter Register)是JVM中一块较小的内存区域,保存着当前线程执行的虚拟机字节码指令的内存地址。Java多线程的实现,其实是通过线程间的轮流切换并分配处理器执行时间的方式来实现的,在任何时刻,处理器都只会执行一个线程中的指令。在多线程场景下,为了保证线程切换回来后,还能恢复到原先状态,找到原先执行的指令,所以每个线程都会设立一个程序计数器,并且各个线程之间不会互相影响,程序计数器为"线程私有"的内存区域。
如果当前线程正在执行Java方法,则程序计数器保存的是虚拟机字节码的内存地址,如果正在执行的是Native方法(非Java方法,JVM底层有许多非Java编写的函数实现),计数器则为空。程序计数器是唯一一个在Java规范中没有规定任何OutOfMemory场景的区域。
虚拟机栈:
虚拟机栈(Java Virtual Machine Stacks)和线程是紧密联系的,每创建一个线程时就会对应创建一个Java栈,所以Java栈也是"线程私有"的内存区域,这个栈中又会对应包含多个栈帧,每调用一个方法时就会往栈中创建并压入一个栈帧,栈帧是用来存储方法数据和部分过程结果的数据结构,每一个方法从调用到最终返回结果的过程,就对应一个栈帧从入栈到出栈的过程。
虚拟机栈是一个后入先出的数据结构,线程运行过程中,只有一个栈帧是处于活跃状态的,被称为"当前活动帧栈",当前活动帧栈始终是虚拟机栈的栈顶元素。
本地方法栈:
本地方法栈(Native Method Stack)和虚拟机栈的作用相似,不过虚拟机栈是为Java方法服务的,而本地方法栈是为Native方法服务的。
方法区:(class+常量池+GC)
方法区(Method Area)是用于存储类结构信息的地方,包括常量池、静态变量、构造函数等类型信息,类型信息是由类加载器在类加载时从类文件中提取出来的。
方法区同样存在垃圾收集,因为用户通过自定义加载器加载的一些类同样会成为垃圾,JVM会回收一个未被引用类所占的空间,以使方法区的空间达到最小。
方法区中还存在着常量池,常量池包含着一些常量和符号引用(加载类的连接阶段中的解析过程会将符号引用转换为直接引用)。
方法区是线程共享的。
堆:(new+GC)
对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。
根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms控制)。
如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
6. [endif]jvm中的GC机制?
1. [endif]回收机制
本地方法栈、程序计数器、虚拟机栈。因为他们的生命周期是和线程同步的,随着线程的销毁,他们占用的内存会自动释放。所以,只有方法区和堆区需要进行垃圾回收,回收的对象就是那些不存在任何引用的对象。
2. [endif]查找算法
跟搜索算法:
从一个叫GC Roots的根节点出发,向下搜索,如果一个对象不能达到GC Roots的时候,说明该对象不再被引用,可以被回收。
[if !supportLists]3. [endif]四种引用
强引用:new出来的对象都是强引用,GC无论如何都不会回收,即使抛出OOM异常。
软引用:只有当JVM内存不足时才会被回收。
弱引用:只要GC,就会立马回收,不管内存是否充足。
虚引用:可以忽略不计,JVM完全不会在乎虚引用,你可以理解为它是来凑数的,凑够” 四大天王”。它唯一的作用就是做一些跟踪记录,辅助finalize函数的使用。
4. [endif]内存分区
新生代(Youn Generation):大致分为Eden区和Survivor区,Survivor区又分为大小相同的两部分:FromSpace和ToSpace。新建的对象都是从新生代分配内存,Eden区不足的时候,会把存活的对象转移到Survivor区。当新生代进行垃圾回收时会出发Minor GC(也称作Youn GC)。
老年代(Old Generation):旧生代用于存放新生代多次回收依然存活的对象,如缓存对象。当旧生代满了的时候就需要对旧生代进行回收,旧生代的垃圾回收称作Major GC(也称作Full GC)。
持久代(Permanent Generation):在Sun 的JVM中就是方法区的意思,尽管大多数JVM没有这一代。
2. 常见的GC算法
复制(Copying):复制算法采用的方式为从根集合进行扫描,将存活的对象移动到一块空闲的区域,如图所示:
当存活的对象较少时,复制算法会比较高效(新生代的Eden区就是采用这种算法),其带来的成本是需要一块额外的空闲空间和对象的移动。
标记-清除(Mark-Sweep):该算法采用的方式是从跟集合开始扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象,并进行清除。标记和清除的过程如下:
上图中蓝色部分是有被引用的对象,褐色部分是没有被引用的对象。在Marking阶段,需要进行全盘扫描,这个过程是比较耗时的。
清除阶段清理的是没有被引用的对象,存活的对象被保留。
标记-清除动作不需要移动对象,且仅对不存活的对象进行清理,在空间中存活对象较多的时候,效率较高,但由于只是清除,没有重新整理,因此会造成内存碎片。
网友评论