当前位置: 首页 > 图灵资讯 > 技术篇> 常考java GC面试题及学习笔记分享

常考java GC面试题及学习笔记分享

来源:图灵教育
时间:2023-04-10 17:24:55

  据说万事开头难。的确,如果你想做一件事,你必须从头开始。开始不仅困难,而且非常重要。对java行业也是如此,java零基础刚开始学习的时候,一定或多或少会遇到困难。虽然每个人都遇到不同的困难,但他们想从事java行业的开头是一样的,那就是面试。面试中必不可少的内容之一是java笔试题。今天,Java GC知识,为大家准备java程序员整理总结了几个常考java GC面试题,还有一些学习笔记可以和大家分享。

  1、为什么Java中会有GC机制?

  答:(1)安全考虑;--for security;(2)减少内存泄漏;--erase memory leak in some degree;(3)减少程序员的工作量。--Programmers don't worry about memory releasing。

  2、既然有GC机制,为什么会有内存泄漏?

  答:理论上Java有垃圾回收机制(GC)没有内存泄漏问题(这也是Java广泛应用于服务器端编程的一个重要原因)。然而,在实际开发中,可能会有无用但可达的对象,这些对象不能被GC回收,因此也会导致内存泄漏。

  3、JavaGC需要回收哪些内存?

  答:内存运行时JVM在运行过程中会有一个数据区来管理内存。它主要包括五个部分:程序计数器(Program CounterRegister);虚拟机栈(VM Stack);本地方法栈(Native Method Stack);方法区(Method Area);堆(Heap)。

  其中,程序计数器、虚拟机堆栈和本地方法堆栈是每个线程的私有内存空间,随线程而生,随线程而死。例如,当类别结构确定堆栈中每个堆栈帧中分配的内存数量时,基本上就知道了在不考虑内存回收的情况下,确定了三个区域的内存分配和回收。

  然而,方法区和堆是不同的。一个接口的多个实现类可能需要不同的内存。只有在程序运行过程中,我们才能知道将创建哪些对象。这部分内存的分配和回收是动态的,GC主要关注这部分内存。总之,GC的主要回收内存是JVM中的方法区和堆。

  4、以下是一个代码会导致内存泄漏的例子import java.util.Arrays; import java.util.EmptyStackException; public class MyStack { private T[] elements; private int size = 0; private static final int INIT_CAPACITY = 16; public MyStack() { elements = (T[]) new Object[INIT_CAPACITY]; } public void push(T elem) { ensureCapacity(); elements[size++] = elem; } public T pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; } private void ensureCapacity() { if (elements.length == size) { elements = Arrays.copyOf(elements,2 * size + 1); } } }

  上面的代码实现了一个栈(先进后出(FILO))乍一看,结构似乎没有明显的问题,它甚至可以通过你写的各种单元进行测试。然而,pop方法存在内存泄漏问题。当我们用pop方法弹出堆栈中的物体时,物体不会被用作垃圾回收,即使堆栈程序不再引用这些物体,因为堆栈内部维护着对这些物体的过期引用(obsolete reference)。在支持垃圾回收的语言中,内存泄漏是非常隐蔽的,这实际上是无意识的对象。如果一个对象的引用被无意识地保留下来,那么垃圾回收器就不会处理对象,也不会处理对象引用的其他对象,即使只有少数对象,也可能导致许多对象被排除在垃圾回收之外,从而对性能产生重大影响,极端情况会导致Disk Paging(物理内存与硬盘的虚拟内存交换数据),甚至导致OutofmemoryEror。

  下面通过对Java8中lambda表达式的初步理解有四个示例:

  (1)Runnnable采用lambda表达式实现, // Java 8 之前: new Thread(new Runnable(){ @Override public void run(){ System.out.println("Before Java8, too much code for too little to do"); }}).start(); //Java 8 方式: new Thread(()->System.out.println("In Java8, Lambda expression rocks !!!")).start(); 输出:too much code, for too little to do, Lambda expression rocks!!

  这个例子向我们展示了Java 8 lambda表达式语法。

  (使用Java 8 lambda表达式事件处理

  如果你用过Swing 在API编程中,您将记住如何编写事件监控代码。这是旧版本中简单匿名的经典用例,但现在不可能了。您可以用lambda表达式编写更好的事件监控代码,如下所示: // Java 8 之前: JButton show = new JButton("Show"); show.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("Event handling without lambda expression is boring"); } }); // Java 8 方式: show.addActionListener((e) -> { System.out.println("Light, Camera, Action !!! Lambda expressions Rocks"); });

  (3)使用Java 8 lambda表达式事件处理 列表迭代采用lambda表达式。

  假如你花了几年时间Java,您知道,对于集合类,最常见的操作是迭代,并将业务逻辑应用于处理订单、交易和事件列表等各种元素。因为Java是命令语言,Java 8之前的所有循环代码都是顺序的,即其元素可以并行处理。如果你想并行过滤,你需要自己写代码,这并不那么容易。通过引入lambda表达式和默认方法,将做什么和做什么分开,这意味着Java集合现在知道如何迭代,并可以在API层面并行处理集合元素。在下面的例子中,我将介绍如何在使用lambda或不使用lambda表达式的情况下迭代列表。您可以看到列表有一种foreach()方法,它可以迭代所有对象,并在其中应用您的lambda代码。 // Java 8 之前: List features = Arrays.asList("Lambdas", "Default Method", "Stream API","Date and Time API"); for (String feature : features) { System.out.println(feature); } // Java 8 之后: List features = Arrays.asList("Lambdas", "Default Method", "Stream API","Date and Time API"); features.forEach(n -> System.out.println(n)); // 使用 Java 8 方法引用更方便,方法引用由::标注双冒号操作符, // 看起来像 C++操作符的作用域分析 features.forEach(System.out::println);

  输出:

  Lambdas Default Method Stream API

  Date and Time API

  列表循环的最后一个例子显示了它是如何工作的Java 引用8中使用方法(method reference)。在Java中,您可以看到C++中的双冒号和范围分析操作符 8用于引用表示方法。

  (4)使用lambda表达式和函数接口Predicatete

  除支持语言层面的函数编程风格外,Java 8还加了一个叫java的包.util.function。它包含多种类型,用于支持Java的函数编程。其中一个是Predicate,使用java.util.function.Predicate函数接口和lambda表达式可以向API方法添加逻辑,用更少的代码支持更多的动态行为。以下是Java 8 Predicate的例子显示了各种常用的过滤和集合数据的方法。Predicate接口非常适合过滤。 public static void main(String[]args){ List languages=Arrays.asList("Java", "Scala","C++", "Haskell", "Lisp"); System.out.println("Languages which starts with J :"); filter(languages, (str)->str.startsWith("J")); System.out.println("Languages which ends with a "); filter(languages, (str)->str.endsWith("a")); System.out.println("Print all languages :"); filter(languages, (str)->true); System.out.println("Print no language : "); filter(languages, (str)->false); System.out.println("Print language whose length greater than 4:"); filter(languages, (str)->str.length()>4); } public static void filter(List names, Predicate condition){ for(String name:names){ if(condition.test(name)){ System.out.println(name+" "); } } } // filter 更好的办法--filter 方法改进 public static void filter(List names, Predicate condition) { names.stream().filter((name)->(condition.test(name))).forEach((name)-> {System.out.println(name + " "); }); }

  可以看到,Stream API的过滤方法也接受了一个Predicate,这意味着我们可以用内联代码代替我们定制的filter()方法,这就是lambda表达式的魔力。此外,Predicate接口还允许多种条件测试。

  总结了以上内容Java GC的四个面试问题和答案,以及Java8中lambda表达式的初步理解。虽然这些内容是更基本的知识点,但我们不应该忽视上述学习内容,因为在java面试中,最重要的调查是基础,只有学习基础才能更深入地学习其他知识。希望有精力和学习兴趣的java程序员能通过学习更多关于java的知识java视频课程我相信朋友们学习后可以学习提高自己的java编程能力,顺利通过面试,将来从事面试java产业发展更好。