首页 文章 精选 留言 我的

精选列表

搜索[面试],共4915篇文章
优秀的个人博客,低调大师

为帝都金三银四准备的Android面试热点题

一、基本概念 (一)、Android四大组件相关基础问题 Activity、Service、BroadcastReceiver、ContentProvider 1、 Activity基本概念:应用程序中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。 2、BroadcastReceiver基本概念:应用程序可以使用它对外部事件进行过滤只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice 来响应它们收到的信息,或者用NotificationManager 来通知用户。通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。 3、service基本概念:一个Service 是一段长生命周期的,没有用户界面的程序,可以用来开发如监控类程序。 4、ContentProvider基本概念:android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。这些数据可以存储在文件系统中、在一个SQLite数据库、或以任何其他合理的方式。其他应用可以通过ContentResolver类(见ContentProviderAccessApp例子)从该内容提供者中获取或存入数据.(相当于在应用外包了一层壳),只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处:统一数据访问方式。 5、四大组件的基本作用 Acitivity :用于显示界面,接收用户输入,和用户交互。 Service :运行于后台无界面的程序,用于在后台完成一下任务,例如:音乐播放等。 BroadCast Receiver :接收系统或应用发出的广播并作出响应,例如:电话的呼入呼出等。 Content Provider :用于把APP本身的数据共享给其他APP,提供本APP数据的存取接口给其他APP Activity 1、Activity的生命周期 Activity的生命周期 *启动Activity:onCreate->onStart->onResume *锁屏或被其它Activity覆盖:onPause->onStop *解锁或由被覆盖状态再回到前台:onRestart->onStart->onResume *跳转到其它Activity或按Home进入后台:onPause->onStop *退回到此Activity:onRestart->onStart->onResume *退出此Activity:onPause->onStop->onDestory *对话框弹出不会执行任何生命周期(注:对话框如果是Activity(Theme为Dialog),还是会执行生命周期的) *从A跳转到B:当B的主题为透明时,A只会执行onPause(A-onPause->B-(onCreate->onStart->onResume)) *从A跳转到B:A-onPause->B-(onCreate->onStart->onResume)-A-onStop(注意是A执行onPause后开始执行B的生命周期,B执行onResume后,A才执行onStop,所以尽量不要在onPause中做耗时操作) *从B返回到A:B-onPause->A-(onRestart->onStart->onResume)-B-(onStop->onDestroy) 2、Activity四种启动模式的区别(LanchMode 的应用场景) ①、standard 模式 这是默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈中。 ②、singleTop 模式 如果在任务的栈顶正好存在该Activity的实例,就重用该实例( 会调用实例的 onNewIntent() ),否则就会创建新的实例并放入栈顶,即使栈中已经存在该Activity的实例,只要不在栈顶,都会创建新的实例。 ③、singleTask 模式 如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的 onNewIntent() )。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。 ④、singleInstance 模式 在一个新栈中创建该Activity的实例,并让多个应用共享该栈中的该Activity实例。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例( 会调用实例的 onNewIntent() )。其效果相当于多个应用共享一个应用,不管谁激活该 Activity 都会进入同一个应用中。 常应用于App主页 3、如何设置Activity的启动模式? 在 AndroidManifest.xml 文件中 Activity 元素的 Android:launchMode 属性。 4、Activity中类似onCreate、onStart运用了哪种设计模式,为什么? 模板模式。每次新建一个Activty时都会覆盖onCreate、onStart等方法,这些方法在父类中就相当于一个模板。 5、如何将一个Activity设置成窗口的样式? 方法一:在AndroidManifest.xml文件中将当前需要改变成窗口样式的属性。 android:theme="@android:style/Theme.Dialog" 方法二:在styles.xml文件中自定义一个主题样式,此主题样式必须继承Dialog的样式. 6、Activity的启动过程 ①、通过Launcher来启动Activity或者通过Activity内部调用startActivity接口来启动新的Activity,都通过Binder进程间通信进入到ActivityManagerService进程中,并且调用ActivityManagerService.startActivity接口; ②、ActivityManagerService调用ActivityStack.startActivityMayWait来做准备要启动的Activity的相关信息; ③、ActivityStack通知ApplicationThread要进行Activity启动调度了,这里的ApplicationThread代表的是调用ActivityManagerService.startActivity接口的进程,对于通过点击应用程序图标的情景来说,这个进程就是Launcher了,而对于通过在Activity内部调用startActivity的情景来说,这个进程就是这个Activity所在的进程了; ④、ApplicationThread不执行真正的启动操作,它通过调用ActivityManagerService.activityPaused接口进入到ActivityManagerService进程中,看看是否需要创建新的进程来启动Activity; ⑤、对于通过点击应用程序图标来启动Activity的情景来说,ActivityManagerService在这一步中,会调用startProcessLocked来创建一个新的进程,而对于通过在Activity内部调用startActivity来启动新的Activity来说,这一步是不需要执行的,因为新的Activity就在原来的Activity所在的进程中进行启动; ⑥、ActivityManagerService调用ApplicationThread.scheduleLaunchActivity接口,通知相应的进程执行启动Activity的操作; ⑦、ApplicationThread把这个启动Activity的操作转发给ActivityThread,ActivityThread通过ClassLoader导入相应的Activity类,然后把它启动起来。 7、Activity、Window 和 View 三者的区别 ①、一个 Activity 构造的时候一定会构造一个 Window(PhoneWindow),并且只有一个。 ②、这个Window会有一个ViewRoot(View、ViewGroup)。 ③、通过addView()加载布局。 ④、WindowMangerService 接收消息,并且回到 Activity 函数,比如onKeyDown()。 Activity 是控制单元---Window 是承载模型---View 是显示视图 8、简述onActivityResult(int requestCode, int resultCode, Intent data)方法的作用? 希望在Activity中得到新打开Activity关闭后返回的数据,则需要使用系统提供的startActivityForResult(Intent intent,int requestCode)方法打开新的Activity,新的Activity关闭后会向前面的Activity传回数据,为了得到传回的数据,必须在最先关闭的Activity中重写onActivityResult(int requestCode, int resultCode,Intent data)方法。 9、内存不足时,怎么保持Activity的一些状态? 在onSaveInstanceState方法中保存Activity的状态,在onRestoreInstanceState或onCreate方法中恢复Activity的状态 10、简述onSaveInstanceState方法作用? ①、用于保存Activity的状态存储一些临时数据 ②、Activity被覆盖或进入后台,由于系统资源不足被kill会被调用 ③、用户改变屏幕方向会被调用 ④、跳转到其它Activity或按Home进入后台会被调用 ⑤、会在onStop之前被调用,和onPause的顺序不固定的 11、简述onRestoreInstanceState(Bundle savedInstanceState)方法作用? ①、用于恢复保存的临时数据,此方法的Bundle参数也会传递到onCreate方法中,你也可以在onCreate(Bundle savedInstanceState)方法中恢复数据 ②、由于系统资源不足被kill之后又回到此Activity会被调用 ③、用户改变屏幕方向重建Activity时会被调用 ④、会在onStart之后被调用 12、onRestoreInstanceState和onCreate的区别是什么? 当onRestoreInstanceState被调用时Bundle参数一定是有值的,不用做为null判断,onCreate的Bundle则可能会为null。官方文档建议在此方法中进行数据恢复。 13、如何安全退出已调用多个Activity的Application? ①、记录打开的Activity:每打开一个Activity,就记录下来。在需要退出时,关闭每一个Activity即可。 ②、发送特定广播:在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可。 ③、递归退出:在打开新的Activity时使用startActivityForResult,然后自己加标志,在onActivityResult中处理,递归关闭。 Fragment 1、Fragment的生命周期 Fragment的生命周期 2、Activity中如何动态的添加Fragment? //1,获取碎片管理器 FragmentManager fragment=getFragmentManager(); //2,碎片的显示需要使用FragmentTransaction类操作 FragmentTransaction transacction=fragment.beginTransaction(); //获取屏幕管理器和默认的显示 Display display=getWindowManager().getDefaultDisplay(); //判断横屏 if(display.getWidth()>display.getHeight()){ //获取java类 Frament1 frament1 = new Frament1(); transacction.replace(android.R.id.content, frament1); }else{ Frament2 frament2 = new Frament2(); transacction.replace(android.R.id.content, frament2); } //3、使用FragmentTransaction必须要commit transacction.commit(); 3、简述Fragment 特点 ①、Fragment可以作为Activity界面的一部分组成出现; ②、可以在一个Activity中同时出现多个Fragment,并且一个Fragment也可以在多个Activity中使用; ③、在Activity运行过程中,可以添加、移除或者替换Fragment; ④、Fragment可以响应自己的输入事件,并且有自己的生命周期,它们的生命周期会受宿主Activity的生命周期影响; ⑤、Fragment可以轻松得创建动态灵活的UI设计,可以适应于不同的屏幕尺寸; ⑥、Fragment 解决Activity间的切换不流畅,轻量切换; 4、Fragment嵌套多个Fragment会出现bug吗? 会出现三种bug:突变的动画效果、被继承的setRetainInstance、错乱的onActivityResult传递 具体分析:http://blog.csdn.net/megatronkings/article/details/51417510

优秀的个人博客,低调大师

2018最新java工程师面试笔试题集锦(一)

找工作有一段时间啦,走了不少弯路;特此提醒,基础很重要,笔试题是打开大门的钥匙;机会虽然很多,但一定要牢牢把握住每一次;拥有选择的权利,不论是心情还是未来都将是无怨无悔的! struts2: Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts 2的Action是一个请求对应一个实例(每次请求时都新new出一个对象),没有线程安全方面的问题。 Struts 2 相比Struts 1的优点: 1、在软件设计上Struts 2 没有像Struts 1那样跟Servlet API 和 struts API 有着紧密的耦合。 Struts 2的应用可以不依赖于Servlet API和Struts API 。 2、Struts 2 提供了拦截器,利用拦截器可以进行AOP编程。 3、Struts 2 提供了类型转换器。 4、Struts 2 提供支持多种表现层技术,如:JSP 、 freeMarker等。 5、Struts 2 的输入校验可以指定方法进行校验。 6、Struts 2 提供了全局范围、包范围和Action范围的国际化资源文件管理实现。 JSP: Jsp包含三个编译指令和七个动作指令。 三个编译指令为:page、include、taglib。 七个动作指令为:jsp:forward、jsp:param、jsp:include、jsp:plugin、jsp:useBean、jsp:setProperty、jsp:getProperty。 〈% @page [language=”Java”] [extends=”package.class”] [import= “package. class I package. *},…”] [session=”true I false”] [buffer=”none I 8kb I size kb” 1 [autoFlush=”true I false”] [isThreadSafe=”true I false”] [info=”text”] [errorPage=”relativeURL”] [contentType=”mimeType[ ;charset=characterSet]” I”text/html;charset= “808859-1”] [isErrorPage=” true I false”] %〉 JSP中有三种形式的脚本,要理解这三种脚本,应该先理解JSP的运行模式,JSP文件有两种形态:设计时的JSP文件和运行时态的Servlet,当用户访问一个JSP文件的时候,这个JSP文件首先会被编译生成Servlet代码,然后这个Servlet会被编译运行,从而产生动态页面,所以JSP中的JAVA脚本都会被作为Servlet类中的代码存在。 1.声明式脚本。jsp中编写方式如下:<%! %>。在<%! %>中的的变量和方法都是作为()类的成员存在的,所以这种脚本称作声明式脚本,用来声明类的成员属性和方法的。 2.普通脚本。又称作Jsp ScriptLet,jsp中编写方式如下:<% %>。在脚本<% %>中的代码是作为servlet类的service方法的一部分存在的,即在<%%>中的变量是局部变量,而不能编写方法,因为java中不允许在方法中再直接定义方法,所以这种脚本称作普通脚本,因为就是普通的java代码。 3.表达式脚本。编写方式如下:<%= %>。在jsp对应的servlet类的service方法中<%= %>中的内容是作为out.print()方法的参数存在的,作用是在浏览器页面上输出内容,所以<%= %>中必须是有结果的表达式用于输出,所以这种脚本称作表达式脚本。 一、String,StringBuffer, StringBuilder 的区别是什么?String为什么是不可变的? 1. String是字符串常量,StringBuffer和StringBuilder是字符串变量。StringBuffer是线程安全的,StringBuilder是非线程安全的。具体来说String是一个不可变的对象,每次修改String对象实际上是创新新对象,并将引用指向新对象。效率很低。StringBuffer是可变的,即每次修改只是针对其本身,大部分情况下比String效率高,StringBuffer保证同步(synchronized),所以线程安全。StringBuilder没有实现同步,所以非线程安全。但效率应该比StringBuffer高。StringBuffer使用时最好指定容量,这样会比不指定容量快30%-40%,甚至比不指定容量的StringBuilder还快。 二、VECTOR,ARRAYLIST, LINKEDLIST的区别是什么? vector是同步的,arraylist和linkedlist不是同步的。底层方面,vector与arraylist都是基于object[]array实现的,但考虑vector线程安全,所以arraylist效率上回比vector较快。元素随机访问上,vector与arraylist是基本相同的,时间复杂度是O(1),linkedlist的随机访问元素的复杂度为O(n)。但在插入删除数据上,linkedlist则比arraylist要快很多。linkedlist比arraylist更占内存,因为linkedlist每个节点上还要存储对前后两个节点的引用。 四、ConcurrentHashMap和HashTable的区别 两者均应用于多线程中,但当HashTable增大到一定程度时,其性能会急剧下降。因为迭代时会被锁很长时间。但ConcurrentHashMap则通过引入分割来保证锁的个数不会很大。简而言之就是HashTable会锁住真个map,而ConcurrentHashMap则只需要锁住map的一个部分。 五、Tomcat,apache,jboss的区别 Tomcat是servlet容器,用于解析jsp,servlet。是一个轻量级的高效的容器;缺点是不支持EJB,只能用于Java应用。 Apache是http服务器(web服务器),类似于IIS可以用来建立虚拟站点,编译处理静态页面。支持SSL技术,支持多个虚拟主机等功能。 Jboss是应用服务器,运行EJB的javaee应用服务器,遵循javaee规范,能够提供更多平台的支持和更多集成功能,如数据库连接,JCA等。其对servlet的支持是通过集成其他servlet容器来实现的。如tomcat。 七、SESSION, COOKIE区别 session数据放在服务器上,cookie则放在客户浏览器上。cookie不太安全,因为可以分析出本地cookie,并进行cookie欺骗,考虑安全应使用session。session会在一定时间内保存在服务器上,当访问增多时,会比较占用服务器的性能,考虑减轻服务器压力则应该使用cookie。单个cookie保持的数据不超过4k,很多浏览器都限制要给站点最多保存20个cookie。 八、Servlet的生命周期 主要分三个阶段:初始化——调用init()方法,响应客户请求阶段——调用service()方法,终止阶段——调用destroy方法。工作原理:客户发送一个请求,servlet调用service方法对请求进行响应,即对请求方式进行匹配,选择调用doGet、doPost方法等,然后进入对于的方法中调用逻辑层的方法,实现对客户的响应。自定义的servlet必须首先servlet接口。 具体生命周期包括:装载Servlet、服务器创建Servlet实例、服务器调用Servlet的init()方法、客户请求到达服务器、服务器创建请求对象、服务创建相应对象、服务器激活Servlet的service方法,请求对象和响应对象作为service()方法的参数、service()方法获得关于请求对象的信息,处理请求,访问其他资源,获得需要的信息、service()方法可能激活其他方法以处理请求,如doGet(),doPost() 九、HTTP 报文包含内容 (请求行,请求头部,请求正文) 请求方法包括GET,POST,HEAD,PUT,TRACE,OPTIONS,DELETE。请求头如:Host、User-Agent、Connection、Accept-Charset等。请求头部的最后会有一个空行,表示请求头部结束,接下来为请求正文,这一行非常重要,必不可少。请求正文为可选部分,如get就没有。 十、Statement与PreparedStatement的区别,什么是SQL注入,如何防止SQL注入 使用PreparedStatement可以提升代码的可读性和可维护性,可以尽最大可能提高性能。因为Statement每次执行一个SQL命令都会对其编译,但PreparedStatement则只编译一次。PreparedStatement就类似于流水线生产。另一方面PreparedStatement可以极大提高安全性:它对传递过来的参数进行了强制参数类型转换,确保插入或查询数据时,与底层数据库格式匹配。 SQL注入:就是通过将sql命令插入到web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意SQL命令。如sql命令:select id from test where name=’1’ or 1=1; drop table test,但用PreparedStatement就可以避免这种问题。 十一、redirect, forward区别 redirect:服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址。所以地址栏显示是新的url。forward是指服务器请求资源,直接访问目标地址url,把响应的内容读取过来并再发送给浏览器,浏览器并不知道资源从哪里来,所以地址栏不变。 redirect不能共享数据,forward转发页面和转发到页面可以贡献request中的数据。redirect用于注销,forward用于登陆。forward效率高于redirect。 十、Statement与PreparedStatement的区别,什么是SQL注入,如何防止SQL注入 使用PreparedStatement可以提升代码的可读性和可维护性,可以尽最大可能提高性能。因为Statement每次执行一个SQL命令都会对其编译,但PreparedStatement则只编译一次。PreparedStatement就类似于流水线生产。另一方面PreparedStatement可以极大提高安全性:它对传递过来的参数进行了强制参数类型转换,确保插入或查询数据时,与底层数据库格式匹配。 SQL注入:就是通过将sql命令插入到web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意SQL命令。如sql命令:select id from test where name=’1’ or 1=1; drop table test,但用PreparedStatement就可以避免这种问题。 十二、关于JAVA内存模型,一个对象(两个属性,四个方法)实例化100次,现在内存中的存储状态,几个对象,几个属性,几个方法。 Java新建的对象都放在堆里,如果实例化100次,堆中产生100个对象,一般对象与其属性和方法属于一个整体,但如果属性和方法是静态的,则属性和方法只在内存中存一份。 mysql连接数据库 Class.forName(“com.sql.jdbc.Driver”); Connection ct=DriverManager.getConnection (“jdbc:mysql://127.0.0.1:3360/first”,”root”,”123”); PreparedStatement ps=ct.preparedStatement(sql); ResultSet rs=ps.executeQuery(); float型float f=3.4是否正确? 不正确。精度不准确,应该用强制类型转换,如下所示:float f=(float)3.4 或float f = 3.4f 在java里面,没小数点的默认是int,有小数点的默认是double; structs 1.是一个框架(基于mvc的一个web框架) 2. (规范化,效率高,可读性好,可维护性增加;) mvc模式(模式是一种思想)(model view control) 将数据的输入,处理(model)和显示分开(jsp) 对mvc的理解不同,写程序时规范不统一,不利维护扩展效率,所以 有了统一的规范structs structs运行原理: 用户登录: 浏览器 web服务器 ActionServlet(struts-config.xml) actionform Loginaction model jsp static表示不要实例化就可以使用。被static修饰的成员变量和成员方法不依赖类特定的实例,被类的所有实例共享。 对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。 一般在需要实现以下两个功能时使用静态变量: 在对象之间共享值时(多个变量共享一个值:如所有学生公用一个变量学费) 方便访问变量时 类变量是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,任何一个该类的对象去修改它时,修改的也是同一个变量。 静态区域块只被执行一次,且自动执行不需实例化。利用静态代码块可以对一些static变量进行赋值。 类变量原则上用类方法去访问;类方法中不许访问非静态变量,反之可以;静态方法中不能用this和super关键字。 类方法属于类相关的、公共的方法 static方法独立于任何实例,static方法必须被实现,而不能是抽象的abstract。 一般类内部的static方法也是方便其它类对该方法的调用。 如果一个成员被声明为static,它就能够在它的类的任何对象创建之前被访问,而不必引用任何对象。 spring Spring是全面的和模块化的。Spring有分层的体系结构,你能选择使用它孤立的任何部分,它的架构仍然是内在稳定的。 Spring作为开源的中间件,独立于各种应用服务器,甚至无须应用服务器的支持,也能提供应用服务器的功能,如声明式事务、事务处理等。 Spring致力于J2EE应用的各层的解决方案,而不是仅仅专注于某一层的方案。可以说Spring是企业应用开发的“一站式”选择,并贯穿表现层、业务层及持久层。然而,Spring并不想取代那些已有的框架,而是与它们无缝地整合。 轻量、控制反转ioc、面向切面、容器、框架、mvc servlet生命周期:创建实例,init初始化,service方法处理,destroy方法销毁。 int 是基本类型,直接存数值 integer是对象,用一个引用指向这个对象(一个类) int i =1; Integer i= new Integer(1);(要把integer 当做一个类看) Integer 是一个类,是int的扩展,定义了很多的转换方法 类似的还有:float Float;double Double;string String等 举个例子:当需要往ArrayList,HashMap中放东西时,像int,double这种内建类型是放不进去的,因为容器都是装object的,这是就需要这些内建类型的外覆类了。 Java中每种内建类型都有相应的外覆类。 Java中int和Integer关系是比较微妙的。关系如下: 1.int是基本的数据类型; 2.Integer是int的封装类; 3.int和Integer都可以表示某一个数值; 4.int和Integer不能够互用,因为他们两种不同的数据类型; 引入的原因:为了在各种类型间转化,通过各种方法的调用。否则 你无法直接通过变量转化。 比如,现在int要转为String int a=0; String result=Integer.toString(a); 在java中包装类,比较多的用途是用在于各种数据类型的转化中。 JVM是一个”桥梁“,是一个”中间件“,是实现跨平台的关键,Java代码首先被编译成字节码文件,再由JVM将字节码文件翻译成机器语言,从而达到运行Java程序的目的。 JVM 有一个选项,可以将使用最频繁的字节码翻译成机器码并保存,这一过程被称为即时编译。这种方式确实很有效, JVM 自己的命令集,JVM 的命令集则是可以到处运行的,因为 JVM 做了翻译,根据不同的CPU ,翻译成不同的机器语言。 Java 中的所有类,必须被装载到 JVM 中才能运行,这个装载工作是由 JVM 中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中。 2. Java中的类大致分为三种: a) 系统类 b) 扩展类 c) 由程序员自定义的类 3. 类装载方式,有两种: a) 隐式装载,程序在运行过程中当碰到通过 new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中。 b) 显式装载,通过 class.forName() 等方法,显式加载需要的类。 原文地址http://www.bieryun.com/691.html

优秀的个人博客,低调大师

java常见面试题及答案 11-20(JVM)

11.JVM内存分哪几个区,每个区的作用是什么? java虚拟机主要分为以下一个区: 方法区: 1. 有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表不发生GC,在这里进行的GC主要是对方法区里的常量池和对类型的卸载 2. 方法区主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代码等数据。 3. 该区域是被线程共享的。 4. 方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该常量池具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也会存在这个常量池中。 虚拟机栈: 1. 虚拟机栈也就是我们平常所称的栈内存,它为java方法服务,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。 2. 虚拟机栈是线程私有的,它的生命周期与线程相同。 3. 局部变量表里存储的是基本数据类型、returnAddress类型(指向一条字节码指令的地址)和对象引用,这个对象引用有可能是指向对象起始地址的一个指针,也有可能是代表对象的句柄或者与对象相关联的位置。局部变量所需的内存空间在编译器间确定 4.操作数栈的作用主要用来存储运算结果以及运算的操作数,它不同于局部变量表通过索引来访问,而是压栈和出栈的方式 5.每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接.动态链接就是将常量池中的符号引用在运行期转化为直接引用。 本地方法栈 本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务。 堆 java堆是所有线程所共享的一块内存,在虚拟机启动时创建,几乎所有的对象实例都在这里创建,因此该区域经常发生垃圾回收操作。 程序计数器 内存空间小,字节码解释器工作时通过改变这个计数值可以选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数器完成。该内存区域是唯一一个java虚拟机规范没有规定任何OOM情况的区域。 12.如和判断一个对象是否存活?(或者GC对象的判定方法) 判断一个对象是否存活有两种方法: 1. 引用计数法 所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一。当一个对象的引用计数器为零时,说明此对象没有被引用,也就是“死对象”,将会被垃圾回收. 引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象A引用对象B,对象B又引用者对象A,那么此时A,B对象的引用计数器都不为零,也就造成无法完成垃圾回收,所以主流的虚拟机都没有采用这种算法。 2.可达性算法(引用链法) 该算法的思想是:从一个被称为GC Roots的对象开始向下搜索,如果一个对象到GC Roots没有任何引用链相连时,则说明此对象不可用。 在java中可以作为GC Roots的对象有以下几种: 虚拟机栈中引用的对象 方法区类静态属性引用的对象 方法区常量池引用的对象 本地方法栈JNI引用的对象 虽然这些算法可以判定一个对象是否能被回收,但是当满足上述条件时,一个对象比不一定会被回收。当一个对象不可达GC Root时,这个对象并 不会立马被回收,而是出于一个死缓的阶段,若要被真正的回收需要经历两次标记 如果对象在可达性分析中没有与GC Root的引用链,那么此时就会被第一次标记并且进行一次筛选,筛选的条件是是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者已被虚拟机调用过,那么就认为是没必要的。 如果该对象有必要执行finalize()方法,那么这个对象将会放在一个称为F-Queue的对队列中,虚拟机会触发一个Finalize()线程去执行,此线程是低优先级的,并且虚拟机不会承诺一直等待它运行完,这是因为如果finalize()执行缓慢或者发生了死锁,那么就会造成F-Queue队列一直等待,造成了内存回收系统的崩溃。GC对处于F-Queue中的对象进行第二次被标记,这时,该对象将被移除”即将回收”集合,等待回收。 13.简述java垃圾回收机制? 在java中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。 14.java中垃圾收集的方法有哪些? 标记-清除: 这是垃圾收集算法中最基础的,根据名字就可以知道,它的思想就是标记哪些要被回收的对象,然后统一回收。这种方法很简单,但是会有两个主要问题:1.效率不高,标记和清除的效率都很低;2.会产生大量不连续的内存碎片,导致以后程序在分配较大的对象时,由于没有充足的连续内存而提前触发一次GC动作。 复制算法: 为了解决效率问题,复制算法将可用内存按容量划分为相等的两部分,然后每次只使用其中的一块,当一块内存用完时,就将还存活的对象复制到第二块内存上,然后一次性清楚完第一块内存,再将第二块上的对象复制到第一块。但是这种方式,内存的代价太高,每次基本上都要浪费一般的内存。 于是将该算法进行了改进,内存区域不再是按照1:1去划分,而是将内存划分为8:1:1三部分,较大那份内存交Eden区,其余是两块较小的内存区叫Survior区。每次都会优先使用Eden区,若Eden区满,就将对象复制到第二块内存区上,然后清除Eden区,如果此时存活的对象太多,以至于Survivor不够时,会将这些对象通过分配担保机制复制到老年代中。(java堆又分为新生代和老年代) 标记-整理 该算法主要是为了解决标记-清除,产生大量内存碎片的问题;当对象存活率较高时,也解决了复制算法的效率问题。它的不同之处就是在清除对象的时候现将可回收对象移动到一端,然后清除掉端边界以外的对象,这样就不会产生内存碎片了。 分代收集 现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生代和老年代。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用标记-整理 或者 标记-清除。 15.java内存模型 java内存模型(JMM)是线程间通信的控制机制.JMM定义了主内存和线程之间抽象关系。线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。Java内存模型的抽象示意图如下: 从上图来看,线程A与线程B之间如要通信的话,必须要经历下面2个步骤: 1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。 2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。 写的很好:http://www.infoq.com/cn/articles/java-memory-model-1 16.java类加载过程? java类加载需要经历一下7个过程:加载 加载时类加载的第一个过程,在这个阶段,将完成一下三件事情: 1. 通过一个类的全限定名获取该类的二进制流。 2. 将该二进制流中的静态存储结构转化为方法去运行时数据结构。 3. 在内存中生成该类的Class对象,作为该类的数据访问入口。 验证 验证的目的是为了确保Class文件的字节流中的信息不回危害到虚拟机.在该阶段主要完成以下四钟验证: 1. 文件格式验证:验证字节流是否符合Class文件的规范,如主次版本号是否在当前虚拟机范围内,常量池中的常量是否有不被支持的类型. 2. 元数据验证:对字节码描述的信息进行语义分析,如这个类是否有父类,是否集成了不被继承的类等。 3. 字节码验证:是整个验证过程中最复杂的一个阶段,通过验证数据流和控制流的分析,确定程序语义是否正确,主要针对方法体的验证。如:方法中的类型转换是否正确,跳转指令是否正确等。 4. 符号引用验证:这个动作在后面的解析过程中发生,主要是为了确保解析动作能正确执行。 准备 准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在Java堆中。 public static int value=123;//在准备阶段value初始值为0 。在初始化阶段才会变为123 。 1 2 解析 该阶段主要完成符号引用到直接引用的转换动作。解析动作并不一定在初始化动作完成之前,也有可能在初始化之后。 初始化 初始化时类加载的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码。 17. 简述java类加载机制? 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成可以被虚拟机直接使用的java类型。 18. 类加载器双亲委派模型机制? 当一个类收到了类加载请求时,不会自己先去加载这个类,而是将其委派给父类,由父类去加载,如果此时父类不能加载,反馈给子类,由子类去完成类的加载。 19.什么是类加载器,类加载器有哪些? 实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。 主要有一下四种类加载器: 1. 启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。 2. 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。 3. 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。 4. 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。 20.简述java内存分配与回收策率以及Minor GC和Major GC 对象优先在堆的Eden区分配。 大对象直接进入老年代. 长期存活的对象将直接进入老年代. 当Eden区没有足够的空间进行分配时,虚拟机会执行一次Minor GC.Minor Gc通常发生在新生代的Eden区,在这个区的对象生存期短,往往发生Gc的频率较高,回收速度比较快;Full Gc/Major GC 发生在老年代,一般情况下,触发老年代GC的时候不会触发Minor GC,但是通过配置,可以在Full GC之前进行一次Minor GC这样可以加快老年代的回收速度。 原文地址http://www.bieryun.com/551.html

优秀的个人博客,低调大师

干货 | 98道常见Hadoop面试题及答案解析(一)

这是一篇hadoop的测试题及答案解析,题目种类挺多,一共有98道题,题目难度不大,对于高手来说,90分以上才是你的追求。 1 单选题 1.1 下面哪个程序负责 HDFS 数据存储。 a)NameNode b)Jobtracker c)Datanode d)secondaryNameNode e)tasktracker 答案 C datanode 1.2 HDfS 中的 block 默认保存几份? a)3 份 b)2 份 c)1 份 d)不确定 答案 A 默认 3份 1.3 下列哪个程序通常与 NameNode 在一个节点启动? a)SecondaryNameNode b)DataNode c)TaskTracker d)Jobtracker 答案 D,此题分析: hadoop 的集群是基于 master/slave 模式,namenode 和 jobtracker 属于 master,datanode 和 tasktracker 属 于 slave , master 只 有 一 个 , 而 slave 有多个SecondaryNameNode 内存需求和 NameNode 在一个数量级上,所以通常 secondary ,NameNode(运行在单独的物理机器上)和 NameNode 运行在不同的机器上。 JobTracker 和 TaskTracker,JobTracker 对应于 NameNode,TaskTracker 对应于 DataNode,DataNode 和 NameNode 是针对数据存放来而言的,JobTracker 和 TaskTracker 是对于 MapReduce 执行而言的,mapreduce 中几个主要概念,mapreduce 整体上可以分为这么几条执行线索:obclient,JobTracker 与 TaskTracker。 JobClient会在用户端通过JobClient类将应用已经配置参数打包成jar文件存储到hdfs,并把路径提交到 Jobtracker,然后由 JobTracker 创建每一个 Task(即 MapTask 和ReduceTask)并将它们分发到各个 TaskTracker 服务中去执行。 JobTracker 是一个 master 服务,软件启动之后 JobTracker 接收 Job,负责调度 Job的每一个子任务 task 运行于 TaskTracker 上,并监控它们,如果发现有失败的 task 就重新运行它。一般情况应该把 JobTracker 部署在单独的机器上。 TaskTracker 是运行在多个节点上的 slaver 服务。TaskTracker 主动与 JobTracker 通信,接收作业,并负责直接执行每一个任务。TaskTracker 都需要运行在 HDFS 的 DataNode上。 1.4 Hadoop 作者 a)Martin Fowler b)Kent Beck c)Doug cutting 答案 C Doug cutting 1.5 HDFS 默认 Block Size a)32MB b)64MB c)128MB 答案:B (因为版本更换较快,这里答案只供参考) 1.6 下列哪项通常是集群的最主要瓶颈: a)CPU b)网络 c)磁盘 IO d)内存 答案:C 磁盘 该题解析: 首先集群的目的是为了节省成本,用廉价的 pc 机,取代小型机及大型机。小型机和大型机有什么特点? cpu 处理能力强 内存够大。所以集群的瓶颈不可能是 a 和 d 网络是一种稀缺资源,但是并不是瓶颈。 由于大数据面临海量数据,读写数据都需要 io,然后还要冗余数据,hadoop 一般备 3份数据,所以 IO 就会打折扣。 1.7 关于 SecondaryNameNode 哪项是正确的? a)它是 NameNode 的热备 b)它对内存没有要求 c)它的目的是帮助 NameNode 合并编辑日志,减少 NameNode 启动时间 d)SecondaryNameNode 应与 NameNode 部署到一个节点。 答案 C 2 多选题 2.1 下列哪项可以作为集群的管理? a)Puppet b)Pdsh c)Cloudera Manager d)Zookeeper 答案:ABD 2.2 配置机架感知的下面哪项正确: a)如果一个机架出问题,不会影响数据读写 b)写入数据的时候会写到不同机架的 DataNode 中 c)MapReduce 会根据机架获取离自己比较近的网络数据 答案 ABC 2.3 Client 端上传文件的时候下列哪项正确? a)数据经过 NameNode 传递给 DataNode b)Client 端将文件切分为 Block,依次上传 c)Client 只上传数据到一台 DataNode,然后由 NameNode 负责 Block 复制工作 答案 B,该题分析: lient 向 NameNode 发起文件写入的请求。 NameNode 根据文件大小和文件块配置情况,返回给 Client 它所管理部分 DataNode 的 信息。 Client 将文件划分为多个 Block,根据 DataNode 的地址信息,按顺序写入到每一个DataNode 块中。 2.4 下列哪个是 Hadoop 运行的模式: a)单机版 b)伪分布式 c)分布式 答案 ABC 2.5 Cloudera 提供哪几种安装 CDH 的方法? a)Cloudera manager b)Tarball c)Yum d)Rpm 答案:ABCD 3 判断题 3.1 Ganglia 不仅可以进行监控,也可以进行告警。( 正确) 分析:此题的目的是考 Ganglia 的了解。严格意义上来讲是正确。ganglia 作为一款最常用的 Linux 环境中的监控软件,它擅长的的是从节点中按照用户的需求以较低的代价采集数据。 但是 ganglia 在预警以及发生事件后通知用户上并不擅长。最新的 ganglia 已经有了部分这方面的功能。但是更擅长做警告的还有 Nagios。Nagios,就是一款精于预警、通知的软件。通过将 Ganglia 和 Nagios 组合起来,把 Ganglia 采集的数据作为 Nagios 的数据源,然后利用 Nagios 来发送预警通知,可以完美的实现一整套监控管理的系统。 3.2 Block Size 是不可以修改的。(错误 ) 分析:它是可以被修改的 Hadoop 的基础配置文件是 hadoop-default.xml,默认建立一个 Job 的时候会建立 Job 的 Config,Config 首先读入 hadoop-default.xml 的配置,然后再读入 hadoop-site.xml 的配置(这个文件初始的时候配置为),hadoop-site.xml 中主要配置需要覆盖的 hadoop-default.xml 的系统级配置。 3.3 Nagios 不可以监控 Hadoop 集群,因为它不提供 Hadoop 支持。(错误 ) 分析:Nagios 是集群监控工具,而且是云计算三大利器之一 3.4 如果 NameNode 意外终止,SecondaryNameNode 会接替它使集群继续工作。 (错误 ) 分析:SecondaryNameNode 是帮助恢复,而不是替代,如何恢复,可以查看 3.5 Cloudera CDH 是需要付费使用的。(错误 ) 分析:第一套付费产品是 Cloudera Enterpris,Cloudera Enterprise 在美国加州举行的Hadoop 大会 (Hadoop Summit) 上公开,以若干私有管理、监控、运作工具加强Hadoop 的功能。收费采取合约订购方式,价格随用的 Hadoop 叢集大小变动。 3.6 Hadoop 是 Java 开发的,所以 MapReduce 只支持 Java 语言编写。(错误 ) 分析:rhadoop 是用 R 语言开发的,MapReduce 是一个框架,可以理解是一种思想,可以使用其他语言开发。 3.7 Hadoop 支持数据的随机读写。(错 ) 分析:lucene是支持随机读写的,而 hdfs 只支持随机读。但是 HBase 可以来补救。HBase提供随机读写,来解决 Hadoop 不能处理的问题。HBase自底层设计开始即聚焦于各种可伸缩性问题:表可以很“高”,有数十亿个数据行;也可以“宽”,有数百万个列;水平分区并在上千个普通商用机节点上自动复制。表的模式是物理存储的直接反映,使系统有可能提高高效的数据结构的序列化、存储和检索。 3.8 NameNode 负责管理 metadata,client 端每次读写请求,它都会从磁盘中读取或则会写入 metadata 信息并反馈 client 端。(错误) 此题分析: NameNode 不需要从磁盘读取 metadata,所有数据都在内存中,硬盘上的只是序列化的结果,只有每次 namenode 启动的时候才会读取。 1)文件写入 Client 向 NameNode 发起文件写入的请求。 NameNode 根据文件大小和文件块配置情况,返回给 Client 它所管理部分 DataNode 的信息。 Client 将文件划分为多个 Block,根据 DataNode 的地址信息,按顺序写入到每一个 DataNode 块中。 2)文件读取 Client 向 NameNode 发起文件读取的请求。 3.9 NameNode 本地磁盘保存了 Block 的位置信息。( 个人认为正确,欢迎提出其它意见) 分析:DataNode 是文件存储的基本单元,它将 Block 存储在本地文件系统中,保存了 Block的 Meta-data,同时周期性地将所有存在的 Block 信息发送给 NameNode。NameNode返回文件存储的 DataNode 的信息。 Client 读取文件信息。 3.10 DataNode 通过长连接与 NameNode 保持通信。(有分歧 ) 这个有分歧:具体正在找这方面的有利资料。下面提供资料可参考。 首先明确一下概念: (1).长连接 Client 方与 Server 方先建立通讯连接,连接建立后不断开,然后再进行报文发送和接收。 这种方式下由于通讯连接一直存在,此种方式常用于点对点通讯。 (2).短连接 Client 方与 Server 每进行一次报文收发交易时才进行通讯连接,交易完毕后立即断开连接。 此种方式常用于一点对多点通讯,比如多个 Client 连接一个 Server. 本文作者:佚名 来源:51CTO

优秀的个人博客,低调大师

大数据技术Hadoop面试题,看看你能答对多少?

单项选择题 1. 下面哪个程序负责 HDFS 数据存储。 a)NameNode b)Jobtracker c)Datanode d)secondaryNameNode e)tasktracker 2. HDfS 中的 block 默认保存几份? a)3 份 b)2 份 c)1 份 d)不确定 3. 下列哪个程序通常与 NameNode 在一个节点启动? a)SecondaryNameNode b)DataNode c)TaskTracker d)Jobtracker 4. Hadoop 作者 a)Martin Fowler b)Kent Beck c)Doug cutting 5. HDFS 默认 Block Size a)32MB b)64MB c)128MB 6. 下列哪项通常是集群的最主要瓶颈 a)CPU b)网络 c)磁盘 d)内存 7. 关于 SecondaryNameNode 哪项是正确的? a)它是 NameNode 的热备 b)它对内存没有要求 c)它的目的是帮助 NameNode 合并编辑日志,减少 NameNode 启动时间 d)SecondaryNameNode 应与 NameNode 部署到一个节点 多选题: 8. 下列哪项可以作为集群的管理工具 a)Puppet b)Pdsh c)Cloudera Manager d)d)Zookeeper 9. 配置机架感知的下面哪项正确 a)如果一个机架出问题,不会影响数据读写 b)写入数据的时候会写到不同机架的 DataNode 中 c)MapReduce 会根据机架获取离自己比较近的网络数据 10. Client 端上传文件的时候下列哪项正确 a)数据经过 NameNode 传递给 DataNode b)Client 端将文件切分为 Block,依次上传 c)Client 只上传数据到一台 DataNode,然后由 NameNode 负责 Block 复制工作 11. 下列哪个是 Hadoop 运行的模式 a)单机版 b)伪分布式 c)分布式 12. Cloudera 提供哪几种安装 CDH 的方法 a)Cloudera manager b)Tar ball c)Yum d)Rpm 判断题: 13. Ganglia 不仅可以进行监控,也可以进行告警。( ) 14. Block Size 是不可以修改的。( ) 15. Nagios 不可以监控 Hadoop 集群,因为它不提供 Hadoop 支持。( ) 16. 如果 NameNode 意外终止,SecondaryNameNode 会接替它使集群继续工作。( ) 17. Cloudera CDH 是需要付费使用的。( ) 18. Hadoop 是 Java 开发的,所以 MapReduce 只支持 Java 语言编写。( ) 19. Hadoop 支持数据的随机读写。( ) 20. NameNode 负责管理 metadata,client 端每次读写请求,它都会从磁盘中读取或则会写入 metadata 信息并反馈 client 端。( ) 21. NameNode 本地磁盘保存了 Block 的位置信息。( ) 22. DataNode 通过长连接与 NameNode 保持通信。( ) 23. Hadoop 自身具有严格的权限管理和安全措施保障集群正常运行。( ) 24. Slave 节点要存储数据,所以它的磁盘越大越好。( ) 25. hadoop dfsadmin –report 命令用于检测 HDFS 损坏块。( ) 26. Hadoop 默认调度器策略为 FIFO( ) 27. 集群内每个节点都应该配 RAID,这样避免单磁盘损坏,影响整个节点运行。( ) 28. 因为 HDFS 有多个副本,所以 NameNode 是不存在单点问题的。( ) 29. 每个 map 槽就是一个线程。( ) 30. Mapreduce 的 input split 就是一个 block。( ) 31. NameNode 的 Web UI 端口是 50030,它通过 jetty 启动的 Web 服务。( ) 32. Hadoop 环境变量中的 HADOOP_HEAPSIZE 用于设置所有 Hadoop 守护线程的内存。它默认是 200 GB。( ) 33. DataNode 首次加入 cluster 的时候,如果 log 中报告不兼容文件版本,那需要 NameNode执行“Hadoop namenode -format”操作格式化磁盘。( ) 本文作者:佚名 来源:51CTO

优秀的个人博客,低调大师

面试 企业shell编程基础问题解决实践(亲测)

问题是选的老男孩老师博客里面的问题,举一反三其中也自己也延伸了两题。 老男孩老师博客的地址http://oldboy.blog.51cto.com/2561410/1718607 言归正传, 1.请用shell或Python编写一个正方形,接收用户输入的数字。 方法:vim zhong1.sh #!/bin/bash read-p"PleaseEnteranumber:"n for((x=1;x<=$n;x++));do for((y=1;y<=$n*2;y++))do echo-n"+" done echo"" done 例如2:用■号实现 (zhong2.sh) 方法:vim zhong2.sh #!/bin/bash read-p"PleaseEnteranumber:"n for((x=1;x<=$n;x++));do for((y=1;y<=$n;y++))do echo-n"■" done echo"" done 2.请用shell或Python编写一个等腰三角形,接收用户输入的数字。 例如:用*号实现 (zhong3.sh) 方法:vim zhong3.sh #!/bin/bash read-p"PleaseEnteranumber:"n for((x=1;x<=$n;x++));do for((z=1;z<=$n-x;z++));do echo-n"" done for((y=1;y<=a+1;y++));do echo-n"*" done echo a=$((a+2)) done 3.请用shell或Python编写一个倒等腰三角形,接收用户输入的数字。(题二的延生) 例如:用*号实现 (zhong33.sh) 方法:vim zhong33.sh (可以在zhong3.sh的基础上行改变一下就可以的出来) #!/bin/bash read-p"PleaseEnteranumber:"n for((x=1;x<=$n;x++));do for((z=0;z<x-1;z++));do echo-n"" done for((y=1;y<=$n-(a+1-$n);y++));do echo-n"*" done echo a=$((a+2)) done 4.请用shell或Python编写一个菱形,接收用户输入的数字。(题二的延生) 例如:用*号实现 (zhong333.sh) 方法:vim zhong333.sh #!/bin/bash read-p"PleaseEnteranumber:"n for((x=1;x<=$n;x++));do for((z=1;z<=$n-x;z++));do echo-n"" done for((y=1;y<=a+1;y++));do echo-n"*" done echo a=$((a+2)) done b=2 for((x=1;x<=$n;x++));do for((z=0;z<=x-1;z++));do echo-n"" done for((y=1;y<=$n-(b+1-$n);y++));do echo-n"*" done echo b=$((b+2)) done 5.请用shell或Python编写一个画直角梯形程序,接收用户输入的参数n,m 例如:用*号实现 (zhong4.sh) 方法:vim zhong4.sh #!/bin/bash for((x=1;x<=$2;x++));do for((y=1;y<=$1+a;y++))do echo-n"+" done a=$((a+1)) echo"" done 以上结果亲测过,能完全实现题目的要求 linux环境:Linux 51master 2.6.32-504.el6.x86_64 #1 SMP Wed Oct 15 04:27:16 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

优秀的个人博客,低调大师

大厂面试真题:“JDK 线程池”如何保证“核心线程”不被销毁?

2 --> 前言 很早之前那个时候练习线程池, 就是感觉线程池类似于 ArrayList 这种集合类结构, 将 Thread类存储, 来任务了就进行消费, 然鹅... 线程包装类 线程池并不是对 Thread 直接存储, 而是对 Thread 进行了一层包装, 包装类叫做 Worker 线程在线程池中的存储结构如下: private final HashSet<Worker> workers = new HashSet<Worker>(); 先看一下 Worker 类中的变量及方法 private final class Worker extends AbstractQueuedSynchronizer implements Runnable { /** * 此线程为线程池中的工作线程 */ final Thread thread; /** * 指定线程运行的第一项任务 * 第一项任务没有则为空 */ Worker(Runnable firstTask) { ... this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this); } /** * 运行传入的 Runnable 任务 */ @Override public void run() { runWorker(this); } } 通过 Worker 的构造方法和重写的 run 得知:线程池提交的任务, 会由 Worker 中的 thread 进行执行调用 addWorker 这里还是要先放一下线程池的执行流程代码, 具体流程如下: public void execute(Runnable command) { ... int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { // 【重点】关注方法 if (addWorker(command, true)) return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (!isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); } else if (!addWorker(command, false)) reject(command); } 我们具体看一下 ThreadPoolExecutor#addWorker private boolean addWorker(Runnable firstTask, boolean core) { ... // worker运行标识 boolean workerStarted = false; // worker添加标识 boolean workerAdded = false; Worker w = null; try { // 调用Worker构造方法 w = new Worker(firstTask); // 获取Worker中工作线程 final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { int rs = runStateOf(ctl.get()); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) throw new IllegalThreadStateException(); // 如无异常, 将w添加至workers workers.add(w); int s = workers.size(); if (s > largestPoolSize) // 更新池内最大线程 largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) { // 启动Woker中thread工作线程 t.start(); // 设置启动成功标识成功 workerStarted = true; } } } finally { // 如果启动失败抛出异常终止, 将Worker从workers中移除 if (!workerStarted) addWorkerFailed(w); } return workerStarted; } 这里需要着重关心下 t.start(), t 是我们 Worker 中的工作线程 上文说到 Worker 实现了 Runnable 接口, 并重写了 run 方法, 所以 t.start() 最终还是会调用 Worker 中的 run() @Override public void run() { runWorker(this); } runWorker ThreadPoolExecutor#runWorker 是具体执行线程池提交任务的方法, 大致思路如下: 1、获取 Worker 中的第一个任务 2、如果第一个任务不为空则执行具体流程 3、第一个任务为空则从阻塞队列中获取任务, 这一点也是核心线程不被回收的关键 runWorker() 中有两个扩展方法, beforeExecute、afterExecute, 在任务执行前后输出一些重要信息, 可用作与监控等... final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { // 【重点】getTask() 是核心线程不被回收的精髓 while (task != null || (task = getTask()) != null) { w.lock(); ... try { beforeExecute(wt, task); Throwable thrown = null; try { task.run(); } ... } finally { afterExecute(task, thrown); } } finally { // 【重点】执行完任务后, 将Task置空 task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { // 退出Worker processWorkerExit(w, completedAbruptly); } } 关键关注下 while 循环, 内部会在执行完流程后将 task 设置为空, 这样就会跳出循环 可以看到 processWorkerExit 是在 finally 语句块中, 相当于 获取不到阻塞队列任务就会去关闭 Worker 线程池是如何保证核心线程获取不到任务时不被销毁呢? 我们继续看一下 getTask() 中是如何获取任务 getTask ThreadPoolExecutor#getTask 只做了一件事情, 就是从线程池的阻塞队列中获取任务返回 private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? for (; ; ) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } int wc = workerCountOf(c); boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } } timed 重点代码从上面截出来, 首先是判断是否需要获取阻塞队列任务时在规定时间返回 /** * 【重点】判断线程是否超时, 这里会针对两种情况判断 * 1. 设置allowCoreThreadTimeOut参数默认false * 如果为true表示核心线程也会进行超时回收 * 2. 判断当前线程池的数量是否大于核心线程数 * * 这里参与了或运算符, 只要其中一个判断符合即为True */ boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; 根据 timed 属性, 判断获取阻塞队列中任务的方式 /** * 【重点】根据timed判断两种不同方式的任务获取 * 1. 如果为True, 表示线程会根据规定时间调用阻塞队列任务 * 2. 如果为False, 表示线程会进行阻塞调用 */ Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); 这里就比较清楚了, 如果 timed 为 True, 线程经过 非核心线程过期时间后还没有获取到任务, 则方法结束, 后续会将 Worker 进行回收 如果没有设置 allowCoreThreadTimeOut 为 True, 以及当前线程池内线程数量不大于核心线程 那么从阻塞队列获取的话是 take(), take() 会 一直阻塞, 等待任务的添加返回 这样也就间接达到了核心线程数不会被回收的效果 getTask流程 “图片本来是要放上面的, 但是画的实在有点不忍直视 ️” 核心线程与非核心线程区别 核心线程只是一个叫法, 核心线程与非核心线程的区别是: 创建核心线程时会携带一个任务, 而非核心线程没有 如果核心线程执行完第一个任务, 线程池内线程无区别 线程池是期望达到 corePoolSize 的并发状态, 不关心最先添加到线程池的核心线程是否会被销毁

优秀的个人博客,低调大师

Netty面试常驻题:你知道Netty的零拷贝机制吗?

理解零拷贝 零拷贝是Netty的重要特性之一,而究竟什么是零拷贝呢?WIKI中对其有如下定义: "Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another. 从WIKI的定义中,我们看到“零拷贝”是指计算机操作的过程中,CPU不需要为数据在内存之间的拷贝消耗资源。而它通常是指计算机在网络上发送文件时,不需要将文件内容拷贝到用户空间(User Space)而直接在内核空间(Kernel Space)中传输到网络的方式。 Non-Zero Copy方式: Zero Copy方式: 从上图中可以清楚的看到,Zero Copy的模式中,避免了数据在用户空间和内存空间之间的拷贝,从而提高了系统的整体性能。Linux中的sendfile()以及Java NIO中的FileChannel.transferTo()方法都实现了零拷贝的功能,而在Netty中也通过在FileRegion中包装了NIO的FileChannel.transferTo()方法实现了零拷贝。 而在Netty中还有另一种形式的零拷贝,即Netty允许我们将多段数据合并为一整段虚拟数据供用户使用,而过程中不需要对数据进行拷贝操作,这也是我们今天要讲的重点。我们都知道在stream-based transport(如TCP/IP)的传输过程中,数据包有可能会被重新封装在不同的数据包中,例如当你发送如下数据时: 有可能实际收到的数据如下: 因此在实际应用中,很有可能一条完整的消息被分割为多个数据包进行网络传输,而单个的数据包对你而言是没有意义的,只有当这些数据包组成一条完整的消息时你才能做出正确的处理,而Netty可以通过零拷贝的方式将这些数据包组合成一条完整的消息供你来使用。而此时,零拷贝的作用范围仅在用户空间中。 以Netty 3.8.0.Final的源代码来进行说明 ###ChannelBuffer接口 Netty为需要传输的数据制定了统一的ChannelBuffer接口。该接口的主要设计思路如下: 1.使用getByte(int index)方法来实现随机访问 2.使用双指针的方式实现顺序访问 每个Buffer都有一个读指针(readIndex)和写指针(writeIndex) 在读取数据时读指针后移,在写入数据时写指针后移 定义了统一的接口之后,就是来做各种实现了。Netty主要实现了HeapChannelBuffer,ByteBufferBackedChannelBuffer等等,下面我们就来讲讲与Zero Copy直接相关的CompositeChannelBuffer类。###CompositeChannelBuffer类 CompositeChannelBuffer类的作用是将多个ChannelBuffer组成一个虚拟的ChannelBuffer来进行操作。 为什么说是虚拟的呢,因为CompositeChannelBuffer并没有将多个ChannelBuffer真正的组合起来,而只是保存了他们的引用,这样就避免了数据的拷贝,实现了Zero Copy。下面我们来看看具体的代码实现,首先是成员变量 private int readerIndex; private int writerIndex; private ChannelBuffer[] components; private int[] indices; private int lastAccessedComponentId; 以上这里列出了几个比较重要的成员变量。其中readerIndex既读指针和writerIndex既写指针是从AbstractChannelBuffer继承而来的;然后components是一个ChannelBuffer的数组,他保存了组成这个虚拟Buffer的所有子Buffer,indices是一个int类型的数组,它保存的是各个Buffer的索引值;最后的lastAccessedComponentId是一个int值,它记录了最后一次访问时的子Buffer ID。 从这个数据结构,我们不难发现所谓的CompositeChannelBuffer实际上就是将一系列的Buffer通过数组保存起来,然后实现了ChannelBuffer 的接口,使得在上层看来,操作这些Buffer就像是操作一个单独的Buffer一样。 创建 接下来,我们再看一下CompositeChannelBuffer.setComponents方法,它会在初始化CompositeChannelBuffer时被调用。 /** * Setup this ChannelBuffer from the list */ private void setComponents(List<ChannelBuffer> newComponents) { assert !newComponents.isEmpty(); // Clear the cache. lastAccessedComponentId = 0; // Build the component array. components = new ChannelBuffer[newComponents.size()]; for (int i = 0; i < components.length; i ++) { ChannelBuffer c = newComponents.get(i); if (c.order() != order()) { throw new IllegalArgumentException( "All buffers must have the same endianness."); } assert c.readerIndex() == 0; assert c.writerIndex() == c.capacity(); components[i] = c; } // Build the component lookup table. indices = new int[components.length + 1]; indices[0] = 0; for (int i = 1; i <= components.length; i ++) { indices[i] = indices[i - 1] + components[i - 1].capacity(); } // Reset the indexes. setIndex(0, capacity()); } 通过代码可以看到该方法的功能就是将一个ChannelBuffer的List给组合起来。它首先将List中得元素放入到components数组中,然后创建indices用于数据的查找,最后使用setIndex来重置指针。这里需要注意的是setIndex(0, capacity())会将读指针设置为0,写指针设置为当前Buffer的长度,这也就是前面需要做assert c.readerIndex() == 0和assert c.writerIndex() == c.capacity()这两个判断的原因,否则很容易会造成数据重复读写的问题。 所以Netty推荐我们使用ChannelBuffers.wrappedBuffer方法来进行Buffer的合并,因为在该方法中Netty会通过slice()方法来确保构建CompositeChannelBuffer是传入的所有子Buffer都是符合要求的。 数据访问 CompositeChannelBuffer.getByte(int index)的实现如下: public byte getByte(int index) { int componentId = componentId(index); return components[componentId].getByte(index - indices[componentId]); } 从代码我们可以看到,在随机查找时会首先通过index获取这个字节所在的componentId既字节所在的子Buffer序列,然后通过index - indices[componentId]计算出它在这个子Buffer中的第几个字节,然后返回结果。 下面再来看一下componentId(int index) 的实现: private int componentId(int index) { int lastComponentId = lastAccessedComponentId; if (index >= indices[lastComponentId]) { if (index < indices[lastComponentId + 1]) { return lastComponentId; } // Search right for (int i = lastComponentId + 1; i < components.length; i ++) { if (index < indices[i + 1]) { lastAccessedComponentId = i; return i; } } } else { // Search left for (int i = lastComponentId - 1; i >= 0; i --) { if (index >= indices[i]) { lastAccessedComponentId = i; return i; } } } throw new IndexOutOfBoundsException("Invalid index: " + index + ", maximum: " + indices.length); } 从代码中我们发现,Netty以lastComponentId既上次访问的子Buffer序号为中心,向左右两边进行搜索,这样做的目的是,当我们两次随机查找的字符序列相近时(大部分情况下都是这样),可以最快的搜索到目标索引的componentId。 写在最后 欢迎大家关注我的公众号【风平浪静如码】,海量Java相关文章,学习资料都会在里面更新,整理的资料也会放在里面。 觉得写的还不错的就点个赞,加个关注呗!点关注,不迷路,持续更新!!!

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。